객체 지향 프로그래밍의 이점 중 하나는 코드의 재사용성이다. 하지만 코드의 재사용성은 절차 지향에서도 가능하다고 이전 블로그에서 따로 설명을 했었다. 분명한 것은 객체 지향만이 가지고 있는 특징은 아니라는 것이다. 코드의 재사용성을 결정하는 것은 순수하게 개발자의 몫이다. 자세한 이야기는 여기 글을 참고하면 좋을 듯하다.


여하튼, 객체 지향의 관점에서 코드의 재사용이 가능하게 해주는 방법은 두 가지가 존재한다. 그 두가지는 아래와 같다.


1) The implementation of inheritance (IS-A relationship)
2) Object Composition (HAS-A relationship)


잠깐 용어를 살펴보자면, Inheritance 는 한 클래스가 다른 클래스의 특성을 상속받는 것이고, Comoposition 은 한 클래스가 다른 클래스의 일부로 합성할 수 있는 것을 의미한다. 



(1) IS-A Relationship : 상속(inheritance) 에 기반

객체 지향 프로그래밍 관점에서 살펴보면, IS-A는 상속을 기반에 두고 있다. 클래스의 상속 혹은 인터페이스의 상속 두 가지 유형이 존재한다.


" A is B Type of thing "


예를 들어서 Apple is a Fruit, Car is a Vehicle etc. 라고 말하는 것들이다. 여기서 중요하게 생각해야 할 것은 상속은 단방향의 성질을 지니고 있다는 것이다.(uni-directional) 우리는 집을 건축물이라고 표현하지만 건축물을 집이라고 말하지 않는 것처럼 말이다.



(2) HAS-A Relationship : 합성(composition) 에 기반

HAS-A는 다른 오브젝트에 대해서 참조 레퍼런스 변수를 가지고 있음을 이야기한다. 예를 들어 집 내부에 화장실이 있는 것처럼 말이다. 



위의 두가지 컨셉을 생각해보고 건축물(Building) 클래스를 아래와 같이 표현할 수 있다. 



위의 그림을 살펴보면 특별한 사항은 없다. Building Class 를 House Class가 상속받고, House Class는 Bathroom class를 해당 레퍼런스로 가지고 있는 것이다. 



내가 참고한 링크에선 작성자는 두 가지 경고를 하고 있다.


(1) " Don't use inheritance just to get code reuse. If all you really want is to reuse code and there is no IS-A relationship in sight use composition "


- " 코드의 재사용을 위해서는 상속을 사용하지 마시오. 만약 네가 코드의 재사용을 원한다면 IS-A 관계보다는 HAS-A 관계를 이용하시오. " 라는 의미로 들린다. 


상속을 하게 된다면 자식 클래스는 부모 클래스에게 종속될 수 밖에 없다. 그렇게 된다면 오브젝트 간의 결합도가 강해져서 이후에 코드 재사용성과 더불어 리팩토링 작업 또한 힘들어 질 것이다. 아마 그에 대한 이야기를 한 것이 아닐까 생각된다.



(2) " Don't use inheritance just to get at polymorphism. If all you really want is a polymorphism, but there is no natural IS-A relationship, use composition with interfaces. "


- " 다형성을 가지기 위해서 상속을 사용하지 마십시오. 만약 네가 다형성을 원한다면 인터페이스와 함께 Composition 을 사용하시오 " 라는 것 같다. 


인터페이스 구현을 하게 되면 인터페이스를 구현하는 클래스는 인터페이스에 있는 선언된 메소드들을 모두 오버라이딩 할 수 밖에 없다. 오버라이딩을 통한 다양한 형태로의 메소드 구현과 함께 다른 객체의 참조 레퍼런스를 가지고 있어서 Flow Of Control 이 가능하다. 결과적으로 이는 다형성의 진가를 발휘할 수 있도록 해주는 것이다. 아마 필자는 종속적인 즉, 의존관계에 대한 결합성에 대해서 우려하기에 이와 같이 말한 것 같다.


[ 추가 & 수정 : 20180202 ]

HAS-A 관계는 스프링의 관점에서 보면 DI(Dependency Injection)를 추구하기 힘들다. 만약에 A Object 가 B Object 에 대한 레퍼런스를 가지고 있고 해당 객체(B) 에 대해서 생성을 하고 있다면 이는 HAS-A 관계이다. A가 B를 포함하고 있는. 


하지만 A와 B를 개별적으로 생성하고, A의 멤버필드로서 B 클래스만 가지고 있다고 생각하자. 그리고 B 객체를 다른 C 라는 클래스에서 생성해서 해당 A 객체에 주입한다고 한다면 A와 B 오브젝트는 서로 다른 관계를 맺고 있다. 


이것이 바로 의존성 주입(DI) 이다. 코드로 표현하면 아래와 같다.


- HAS-A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Main {
    public static void main(String[]args){
        Person gildong = new Person();
        gildong.setPencil();
        gildong.write();
    }
}
 
// 사람
class Person{
    private Pencil pencil;
    
    public Person(){}
    
    public void write(){
        System.out.println(pencil.toString());
    }
    
    public void setPencil(){
        this.pencil = new Pencil();
    }
}
 
// 연필
class Pencil{
    private String attribute = "4B";
    private String maker = "donga";
    
    @Override
    public String toString() {
        return "Pencil [attribute=" + attribute + ", maker=" + maker + "]";
    }
}
cs


- DI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class Main {
    public static void main(String[]args){
        Person gildong = new Person();
        PencilVase pencilVase = new PencilVase();
        Pencil pencil = new Pencil();
        
        pencilVase.deployPencil(pencil);
        gildong.setPencilVase(pencilVase);
        gildong.setPencil(pencilVase.getPencil());
    }
}
 
// 필통
class PencilVase{
    private Pencil pencil;
    
    public PencilVase(){}
    
    public void deployPencil(Pencil pencil){
        this.pencil = pencil;
    }
    
    public Pencil getPencil(){
        return pencil;
    }
}
 
// 사람
class Person{
    private Pencil pencil;
    private PencilVase pencilVase;
    
    public Person(){}
    
    public void write(){
        System.out.println(pencil.toString());
    }
 
    public void setPencil(Pencil pencil){
        this.pencil = pencil;
    }
    
    public void setPencilVase(PencilVase pencilVase){
        this.pencilVase = pencilVase;
    }
}
 
// 연필
class Pencil{
    private String attribute = "4B";
    private String maker = "donga";
    
    @Override
    public String toString() {
        return "Pencil [attribute=" + attribute + ", maker=" + maker + "]";
    }
}
cs


사람과 연필간의 관계를 중간에 필통이라는 매개가 연결시켜준다. 그리고 연필에 대한 의존성을 사람에게 주입시켜주고 있다. 


Posted by doubler
,