abstract factory는 '추상적인 공장'이란 뜻으로, 밑의 그림과 같이 여러 개의 concrete Product 추상화시킨 것이다. 따라서 구체적인 구현은 concrete Product 클래스에서 이루어진다. abstract factory에서는 사용자에게 인터페이스(API)를 제공하고, 인터페이스만 사용해서 부품을 조립하여 만든다. 즉 추상적인 부품을 조합해서 추상적인 제품을 만든다.

 

 

2019/08/19 - [디자인패턴] - 디자인패턴 - 팩토리 메소드 패턴(Factory Method pattern)

 

디자인패턴 - 팩토리 메소드 패턴(Factory Method pattern)

factory는 '공장'이란 뜻이고, 공장은 물건을 만드는 곳이다. 여기서 물건에 해당되는 것이 바로 인스턴스이다. factory method 패턴은 상위 클래스에서 객체를 생성하는 인터페이스를 정의하고, 하위 클래스에서..

coding-start.tistory.com

 

이전 포스팅에서 다루어봤던 팩토리 메소드 패턴의 확장판이라고 볼수 있는 것이 추상 팩토리 패턴이다. 팩토리 메소드 패턴을 다시 떠올려보자. 엘리베이터는 특정 스케쥴링 방식에 따라 다른 방식으로 동작한다. 즉, 스케쥴러 클래스의 선택에 따라 엘리베이터의 동작이 달라지는 것이다. 하지만 이런 경우를 생각해보자 !

 

엘리베이터 제조업체가 여러개가 있다. LG,Samsung,Hundai 등의 많은 제조업체가 있고 해당 업체의 엘리베이터를 사용하려면 각각 제조업체의 모터,문,렘프 등을 사용해야한다.(이번 예제에서는 각 업체마다 모터, 문만 다룬다.) 경우에 따라 부품의 수가 더 많아 질 수도 있고 제조업체도 더욱 많아 질 수도 있다. 

 

만약 기존에 사용했던 팩토리 메소드 패턴을 사용한다면 어떻게 될까? Motor,Door라는 인터페이스가 있고 각각을 구현한 LGMotor,LGDoor,SamsungMotor,SamsungDoor 등 업체마다 다른 부품에 해당하는 클래스를 생성해주어야 하고 각각을 생성해주는 팩토리 클래스를 만들어주어야 한다.(LGMotorFactor,SamsungMotorFactory,HundaiFactory...) 3개의 업체라면 객체 생성을 담당하는 팩토리 클래스가 6개가 생성된다.(MotorFactory - LG,Samsung,Hundai / DoorFactory - LG,Samsung,Hundai) 딱 봐도 비효율적일 것같다라는 느낌이 든다.

 

여기서 핵심은 특정 제조업체의 엘리베이터를 이용하면 그들의 부품만 사용한다는 뜻이다. 즉, 관련있는 객체(모터,문)들이 제조업에 따라 같이 생성이 된다는 것이다. 팩토리 메소드 패턴은 한종류의 객체를 생성하는 롤을 갖고 있다면 추상 팩토리 패턴은 다수의 연관있는 객체들의 일련의 생성을 담당하고 있다고 보면 된다. 바로 구현해보자.

 

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
public abstract class AbstractFactory {
    
    public abstract Motor createMotor();
    public abstract Door createDoor();
    
    enum Vendor{
        LG,SAMSUNG,HUNDAI;
    }
    
    public static AbstractFactory getFactory(Vendor vendor) {
        
        AbstractFactory factory = null ;
        
        switch (vendor) {
        case LG:
            factory = LGElevatorFactory.getInstance();
            break;
        case SAMSUNG:
            factory = SamsungElevatorFactory.getInstance();
            break;
        case HUNDAI:
            factory = HundaiElevatorFactory.getInstance();
            break;
        default:
            break;
        }
        
        return factory;
        
    }
    
}
 
public class LGElevatorFactory extends AbstractFactory{
    
    private static final LGElevatorFactory FACTORY = new LGElevatorFactory();
    
    public static AbstractFactory getInstance() {
        return FACTORY;
    }
 
    @Override
    public Motor createMotor() {
        return new LGMotor();
    }
 
    @Override
    public Door createDoor() {
        return new LGDoor();
    }
    
}
cs

 

추상 팩토리 클래스를 하나 만들어주고 각 제조사에 해당하는 팩토리클래스를 생성해준다. 이전 팩토리메소드 패턴과 같이 부품마다 별도의 팩토리 클래스를 두는 것이 아니라 각 제조사마다 팩토리 메소드를 만들어서 관련있는 모든 부품에 대한 생성을 제조사 팩토리 클래스에 위임한다. 또한 모든 제조사의 팩토리 메소드는 동일한 타입의 부품을 생성하는 팩토리 메소드를 가지니, 추상 클래스도 추상 팩토리 클래스를 만들어 상속받게 한다.

 

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public abstract class Door {
    
    enum DoorStatus{
        OPEN,CLOSE;
    }
    
    public Door() {
        this.status=DoorStatus.CLOSE;
    }
    
    private DoorStatus status;
 
    public DoorStatus getStatus() {
        return status;
    }
 
    public void setStatus(DoorStatus status) {
        this.status = status;
    }
    
    public void open() {
        if(this.status.equals(DoorStatus.OPEN)) return;
        this.status=DoorStatus.OPEN;
        System.out.println("문을 엽니다.");
    }
    
    public void close() {
        if(this.status.equals(DoorStatus.CLOSE)) return;
        this.status=DoorStatus.CLOSE;
        System.out.println("문을 닫습니다.");
    }
    
}
 
public abstract class Motor {
    
    private Door door;
    private MotorStatus status;
    
    public Motor() {
        this.status=MotorStatus.STOP;
    }
    
    enum MotorStatus{
        MOVING,STOP;
    }
    
//템플릿 메소드 패턴 적용.
    public void move() {
        //모터가 이미 움직이고 있다면 아무런 행동을 하지 않는다.
        if(getStatus().equals(MotorStatus.MOVING)) return;
        //문이 열려있다면 닫는다.
        if(door.getStatus().equals(DoorStatus.OPEN)) door.close();
        doMove();
        //모터를 이동중으로 설정한다.
        setStatus(MotorStatus.MOVING);
    }
    
    /*
     * 업체마다 doMove의 행동은 다르다.
     */
    public abstract void doMove();
 
    public MotorStatus getStatus() {
        return status;
    }
 
    public void setStatus(MotorStatus status) {
        this.status = status;
    }
 
    public Door getDoor() {
        return door;
    }
 
    public void setDoor(Door door) {
        this.door = door;
    }
    
    
    
}
cs

 

각 부품에 대한 추상클래스이다. 각 제조사 부품마다 동일한 행동을 하지만 자세한 구동 프로세스가 다를 수 있으므로 모터 추상클래스는 템플릿 메소드 패턴을 이용하여 상속받게 하였다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
public class LGMotor extends Motor{
 
    @Override
    public void doMove() {
        System.out.println("LG motor 가동");
    }
    
    
}
 
public class LGDoor extends Door{
 
}
cs

 

LG모터와 문 코드이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class AbstractFactoryMain {
 
    public static void main(String[] args) {
        
        AbstractFactory factory = AbstractFactory.getFactory(Vendor.LG);
        
        Motor motor = factory.createMotor();
        Door door = factory.createDoor();
        motor.setDoor(door);
        
        door.open();
        motor.move();
        door.open();
    }
 
}
cs

 

모터와 문에 대한 클래스의 구현체를 생각할 필요가 없어졌다. 단순히 추상 팩토리 클래스에 벤더값만 전달하면 모든 연관있는 부품에 대한 생성은 각 업체마다의 팩토리 클래스가 담당하게 된다.

 

어떻게 보면 팩토리 메소드 패턴의 확장판이라고 볼수 있다. 추상 팩토리 패턴은 일련의 관련있는 객체의 생성을 캡슐화 혹은 추상화 하기 위해 사용하는 패턴이다. 이제는 클라이언트 코드에서 업체가 바뀌어도 코드의 변경을 필요 없게 되었다. 단순히 어떠한 업체인지 벤더 값만 전달해주고 팩토리 클래스가 생성해주는 모터와 문의 구현체를 몰라도되고 단순히 사용만 하면 된다.

posted by 여성게
: