본문 바로가기
JAVA

Has A 관계

by 영카이브 2024. 1. 11.

의존성(dependency) 이란?

 

의존성(dependency)은 하나의 소프트웨어 요소가 다른 요소에 의존하는 관계를 나타낸다. 의존성은 한 객체가 다른 객체에 의존하여 해당 객체의 기능이나 자원을 사용하는 상태를 의미한다.만 관계를 가지고 있다고 하려면 그것은 최소한 멤버의 속성(Has - A 관계)으로 있거나 상속(Is - A 관계)을 해야만 관계가 있다고 본다.

 

 

Has A 관계(포함)란?

 

캡슐이 다른 캡슐의 객체를 이용하기위해 멤버로 가지고 있는 상태를 말한다

  • Has A 관계를 형성하려면
    • 한 클래스가 다른 클래스의 객체를 멤버 변수로 가져야 한다.
    • 해당 클래스가 그 객체를 사용하는 메서드를 포함하고 있어야한다
    • public class Engine {
          public void start() {
              System.out.println("start");
          }
      
          // 엔진에 관련된 다른 메서드들...
      }
      
      public class Car {
          private Engine engine;
      
          public Car(Engine engine) {
              this.engine = engine;
          }
      
          public void start() {
              System.out.println("start");
              engine.start(); // 엔진의 start 메서드 호출
          }
      
          // Car 클래스에서 엔진 객체를 활용하는 다른 메서드들...
      }
  • Has A 관계가 아닌 경우
    • 한 클래스가 다른 클래스의 객체를 멤버 변수로 가지고 있지만, 해당 객체를 사용하는 메서드가 없다면 Has A 관계가 형성되지않는다.
    • public class Car {
          private Engine engine;
      
          public Car(Engine engine) {
              this.engine = engine;
          }
      
          // Car 클래스에서 Engine 객체의 메서드를 호출하거나 활용하는 로직이 없음
          // 다른 메서드들...
      }
      
      public class Engine {
          public void start() {
              System.out.println("start");
          }
      
          // 엔진에 관련된 다른 메서드들...
      }

 

 

Has A 관계 모델

  • Composition Has A (합성 Has-A 관계)
    •  A클래스가 B클래스에대한 객체를 가질 때 집합과 상관없이 B에대한 객체는 A 객체가 생성될 때 같이 한번에 가지고 있는 것을 말한다. 
    • 즉 Compostion은 강한 연관성을 나타내며, 두 객체 간의 생명주기가 밀접하게 연결되어있다.
// Engine 클래스
public class Engine {
    public void start() {
        System.out.println("start");
    }

    // 엔진에 관련된 다른 메서드들...
}

// Car 클래스
public class Car {
    private Engine engine;  // Composition Has-A 관계, Car는 Engine을 포함하고 있음

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void drive() {
        System.out.println("drive");
        engine.start();  // Engine 객체를 사용하는 예시
    }

    // 다른 Car 클래스의 메서드들...
}
  • Car 클래스가 Engine 클래스를 멤버 변수로 가지고 있다.
  • Car 객체가 Engine 객체를 포함하고 있으므로 Composition 관계이다.
  • 따라서 Car 객체가 소멸될 때 Engine 객체도 함께 소멸한다.

 

  • Aggreagation Has A (집합 - Has-A 관계)
    • A에서 B에 대한 객체를 가질 때 집합적으로 갖는다. 즉 A를 만들 때 B에 대한 객체가 처음엔 없을 수도 있으며 필요할 때마다 누적해서 사용한다.
    • 즉 Aggregation은 느슨한 연관성을 가지며, 두 객체 간의 생명주기가 서로 독립적이다.
public class University {
    private List<Student> students; // Aggregation 관계, University는 Student들을 포함하고 있지만, Student들은 독립적으로 존재 가능
}

public class Student {
    private String name;
    private int studentId;

    // 각 학생에 대한 다양한 정보와 메서드들...
}​
  • University 클래스가 Student 클래스를 멤버 변수로 가지고 있다.
  • Student 객체는 자신의 이름과 학생 ID 등을 가지고 있어서 독립적으로 의미가 있다.
  • University 객체가 소멸되어도 각 Student 객체들은 여전히 자신의 정보를 유지하고 다른 맥락에서 사용될 수 있다.

 

 

Composition Has A 와 Aggreagation Has A 구체적 예시

 

ex1)

Composition Has A 예시 : ExamConsole이 만들어질 때 생성자에서 ExamList 객체가 같이 생성된다.

public class ExamConsole {
	// Composition Has A 일체형
	private ExamList list;

	public ExamConsole() {
		list = new ExamList();
	}
    // 이후 로직
}

 

 

ex2)

Aggreagation Has A 예시 : ExamList가 만들어질 때 생성자에서 new Exam[3]가 만들어진다. 그러나 new Exam[3] 은 Exam객체가 생성되는것이 아니라 Exam객체를 참조하기위한 참조변수 3개짜리가 만들어지는 것이다. 

public class ExamList {
	private Exam[] exams;
	private int current;
    
	public ExamList() {
		exams = new Exam[3]; // Aggregation Has A
		current=0;
	}
	
	// 이후 로직

}

 

 

ex3)

1. Composition Has A 예시 : University 객체가 소멸되면 각 Student 객체도 소멸된다.

public class Main {
    public static void main(String[] args) {
        // 대학 생성
        University university = new University("My University");

        // 대학 객체 소멸
        university = null;
    }
}

class University {
    private String name;
    private List<Student> students; // Composition Has-A 관계, University는 Student들을 생성하여 가지고 있으며, University가 소멸되면 Student들도 함께 소멸됨

    public University(String name) {
        this.name = name;
        this.students = createStudents(); // Composition의 일부로서, University가 생성될 때 Student를 생성
    }

    private List<Student> createStudents() {
        // Composition의 일부로서, University가 생성될 때 Student를 생성
        List<Student> students = new ArrayList<>();
        students.add(new Student("가현", 1));
        students.add(new Student("나현", 2));
        return students;
    }

    public String getName() {
        return name;
    }

    public List<Student> getStudents() {
        return new ArrayList<>(students); // 복사된 리스트 반환
    }
}

class Student {
    private String name;
    private int studentId;

    public Student(String name, int studentId) {
        this.name = name;
        this.studentId = studentId;
    }

    public String getName() {
        return name;
    }

    public int getStudentId() {
        return studentId;
    }
}

 

 

2. Aggreagation Has A 예시 : University 객체가 소멸되어도 각 Student 객체들은 여전히 자신의 정보를 유지한다는 예시

public class Main {
    public static void main(String[] args) {
        // 학생들 생성
        Student student1 = new Student("가현", 1);
        Student student2 = new Student("나현", 2);

        // 대학 생성 및 학생들 추가
        List<Student> students = new ArrayList<>();
        students.add(student1);
        students.add(student2);

        University university = new University("My University", students);

        // 대학 객체 소멸
        university = null;

        // 각 학생 객체 정보 출력
        System.out.println("Student1 Name: " + student1.getName() + ", Student1 ID: " + student1.getStudentId());
        System.out.println("Student2 Name: " + student2.getName() + ", Student2 ID: " + student2.getStudentId());
    }
}

class University {
    private String name;
    private List<Student> students; // Aggregation 관계, University는 Student들을 가지고 있지만, Student들은 독립적으로 존재할 수 있음

    public University(String name, List<Student> students) {
        this.name = name;
        this.students = new ArrayList<>(students); // 복사해서 저장
    }

    public String getName() {
        return name;
    }

    public List<Student> getStudents() {
        return new ArrayList<>(students); // 복사된 리스트 반환
    }
}

class Student {
    private String name;
    private int studentId;

    public Student(String name, int studentId) {
        this.name = name;
        this.studentId = studentId;
    }

    public String getName() {
        return name;
    }

    public int getStudentId() {
        return studentId;
    }
}

 

ex3 으로 알 수 있는 것은

일반적으로 직접 객체에 값을 넣어주는 것이 Composition 관계에 해당하고

미리 생성된 객체를 전달받아 설정하는 것은 Aggregation 관계에 해당한다.


 

현 코드를 보자 

public class Program {
	public static void main(String[] args) {
		InventoryConsole inventory = new InventoryConsole(); 

		int menu; 
		boolean keepLoop=true;
		
		while(keepLoop) {
			menu=inputMenu();
			switch(menu) 
			{
			case 1:
				inventory.inputInventory();
				break; 
			case 2 :
				inventory.outputInventory();
				break;
			case 3 : 
				System.out.println("종료");
				keepLoop=false;
				break;
			default :
				System.out.println("메뉴는 1~3번까지 입니다.");
			}	
		}
	}

	private static int inputMenu() {
		Scanner sc = new Scanner(System.in);
		System.out.println("1.상품 입력 ");
		System.out.println("2.재고 출력 ");
		System.out.println("3.종료");
		int menu=sc.nextInt();
		return menu;
	}
}
public class InventoryConsole {
	Inventory inventory = new Inventory();
	
	public void inputInventory() {
		Scanner sc = new Scanner(System.in);
		System.out.println("상품 입력");
		
		String name; 
		int price, quantity;
		
		System.out.printf("상품 이름 : "); 
		name=sc.next();
		do {
			System.out.print("가격 : ");
			price=sc.nextInt();
			if(price<0) {
				System.out.println("가격은 0보다 커야합니다.");
			}
		}while(price<0);
		do {
			System.out.print("수량 : ");
			quantity=sc.nextInt();
			if(quantity<0) {
				System.out.println("수량은 0보다 커야합니다.");
			}
		}while(quantity<0);
		
		Product product= new Product(name, price, quantity); 
		// 더하는 메서드 
		inventory.add(product);
		
	}
	
	// 재고목록 총 출력 
	public void outputInventory() {
		this.outputInventory(inventory.size());
	}

	// 재고목록 필요범위까지만 출력
	public void outputInventory(int size) {
		System.out.println("상품 출력");
		for( int i=0; i<size; i++) {
			// 재고목록에서 상품을 꺼내는 메서드 get()
			Product product = inventory.get(i);
			String name = product.getName(); 
			int price = product.getPrice();
			int quantity = product.getQuantity();
			int total = product.calculateTotal(); // 상품별 총 가격 계산
			System.out.println( "상품 이름 : " + name);
			System.out.println( "상품 가격 : " + price);
			System.out.println( "상품 수량 : " + quantity);
			System.out.println( "상품별 총 가격 : " + total);
		}		
	}
}
public class Inventory {
	private Product[] products;
	private int current; 
	
	public Inventory() {
		this(5);
	}	
	
	public Inventory(int size) {
		this.products = new Product[size]; 
		this.current=0; 
	}

	public Product[] getProducts() {
		return products;
	}

	public void setProducts(Product[] products) {
		this.products = products;
	}

	public int getCurrent() {
		return current;
	}

	public void setCurrent(int current) {
		this.current = current;
	}

	public int size() {
		return this.current;
	}

	// 상품을 재고목록에 추가 
	public void add(Product product) {
		Product[] products = this.products;
		int size = this.size(); 
		if(products.length==size) {
			Product[] temp = new Product[size+3]; 
			for( int i=0; i<size; i++) {
				temp[i]=products[i]; 
			}
			products = temp;
			this.products=products;
		}
		products[size]=product;
		current++;
	}

	public Product get(int i) {
		return products[i];
	}
}
public class Product {
	String name;
	int price; 
	int quantity;
	
	public Product() {
		this("Undefined", 0, 0);
	}
	
	public Product(String name, int price, int quantity) {
		this.name = name;
		this.price = price;
		this.quantity = quantity;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public int getQuantity() {
		return quantity;
	}
	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}

	public int calculateTotal() {
		return price * quantity;
	}
}

 

  • UI(InventoryConsole) - 목록 관리(Inventory) - 실질적 데이터 (Product) 로 포함관계로 부품(멤버변수)으로 가지고 있다.
  • InventoryConsole --- Product가 사용관계다. 
    • Program에서는 InventoryConsole의 input()과 output() 사용한다.
    • InventoryConsole에서는 Inventorydml add(product)와 get()을 사용한다.
    • Inventory에서는 Product의 calculateTotal()을 사용하지 않는다.
  • 사용하는 관계와 구성하는 관계가 일치해야 하므로  Inventory , 목록 관리 역할을 하는 클래스는 멤버변수로 객체를 가지고 있지만 메서드를 사용하지 않아 생략한다
  • InventoryConsole과 Product는 Aggreagation Has A 관계이다.
  • Program이라는 클래스안에는 main함수가 있고 Program클래스의 멤버변수는 없다. main함수 안에 Inventory Console객체가 존재한다. 이럴 경우 Program과 InventoryConsole 과의 관계는 단순 dependency관계이다.

 

 

참고 자료 출처 : https://www.youtube.com/watch?v=6wKyPg9rxtw&list=PLq8wAnVUcTFX4E2NplMvJfqlcgAeF_BxK&index=12

'JAVA' 카테고리의 다른 글

IS A 상속  (0) 2024.01.16
코드 재사용  (0) 2024.01.14
UI코드 분리 하기  (0) 2024.01.10
Setter 대신 Overload 생성자 이용하기  (0) 2024.01.09
Getter 와 Setter  (0) 2024.01.07