의존성(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 |