추상 팩토리 패턴 (Abstract Factory Pattern)

반응형
728x90
반응형

 

수강완료한 강의 복습해보자
(코딩으로 학습하는 GoF의 디자인 패턴)


추상 팩토리 (Abstract Factory)

  • 서로 관련있는 여러 객체를 만들어주는 인터페이스
  • 구체적으로 어떤 클래스의 인스턴스를(concrete product)를 사용하는지 감출 수 있다.

 

 

적용 전 코드 (Before)

Anchor.java
public interface Anchor {

}

 

WhiteAnchor.java
public class WhiteAnchor implements Anchor {

}

 

Wheel.java
public interface Wheel {

}

 

WhiteWheel.java
public class WhiteWheel implements Wheel {

}

 

Ship.java
@Getter
@Setter
@ToString
public class Ship {
  private String name;
  private String color;
  private String logo;

  private Wheel wheel;
  private Anchor anchor;
}

 

Whiteship.java
public class Whiteship extends Ship {
  public Whiteship() {
    setName("whiteship");
    setLogo("\\uD83D\\uDEE5");
    setColor("white");
  }
}

 

ShipFacotry.java
public interface ShipFactory {
    /**
     * 추상메서드
     * 하위클래스가 정의해야하는 메서드
     * @return
     */
    Ship createShip();
}

 

WhiteshipFacotry.java
public class WhiteshipFactory extends ShipFactory {
    @Override
    public Ship createShip() {
        Ship ship = new Whiteship();
        /* 구현클래스 변경시 계속적으로 코드 변경이 발생한다. */
        ship.setAnchor(new WhiteAnchor());
        ship.setWheel(new WhiteWheel());
        return ship;
    }
}

 

1) 요구사항

Ship 의 구성품인 Anchor, wheel을 설정하여 Ship을 생성한다.

 

2) 문제점

ShipFacotry의 구현 클래스인 WhiteshipFactory에서 Anchor, Wheel 을 직접 셋팅해주고 있다.

만약 구성품의 요건이 변경된다면 구현 클래스에 계속적인 코드 변경이 발생한다.

Ship ship = new Whiteship();
ship.setAnchor(new WhiteAnchor());
ship.setWheel(new WhiteWheel());
return ship;

 

3) 추가요건

구성품 Anchor, Wheel의 Pro 버전이 출시되었습니다. Pro 버전의 Ship을 추가로 생성해주세요.

현재 코드로는 메서드를 추가하는 방법 또는 메서드 매개변수로 구분값을 받아서 그에 따른 구성품을 가진 Ship을 생성해줘야한다. 메서드를 추가하는 방법은, Pro 버전 이외의 또 다른 버전이 추가된다면 또 메서드를 추가해야한다. 구분값을 사용한다면 위 팩토리 메서드 패턴에서 봤듯이, 구분값 추가에 따른 if문 등의 분기 로직이 계속해서 추가되어야한다. 이는 확장성에 열려있지 못하며, 변경에 유연하지 못한 코드라는 증거다.

 

 

 

적용 후 코드 (After)

WhiteAnchorPro.java
public class WhiteAnchorPro implements Anchor {

}

 

WhiteWheelPro.java
public class WhiteWheelPro implements Wheel {

}

 

ShipFactory.java
public interface ShipFactory {
    /**
     * 추상메서드
     * 하위클래스가 정의해야하는 메서드
     * @return
     */
    Ship createShip();
}

 

DefaultShipFactory.java
public abstract class DefaultShipFactory implements ShipFactory {

}

 

ShipPartsFactory.java
public interface ShipPartsFactory {
    Anchor createAnchor();
    Wheel createWheel();
}

 

WhiteshipPartsFactory.java
public class WhiteshipPartsFactory implements ShipPartsFactory {
  @Override
  public Anchor createAnchor() {
    return new WhiteAnchor();
  }

  @Override
  public Wheel createWheel() {
    return new WhiteWheel();
  }
}

 

WhitePartsProFactory.java
public class WhitePartsProFactory implements ShipPartsFactory {
  @Override
  public Anchor createAnchor() {
    return new WhiteAnchorPro();
  }

  @Override
  public Wheel createWheel() {
    return new WhiteWheelPro();
  }
}

 

WhiteshipFactory.java
public class WhiteshipFactory extends DefaultShipFactory {

  private ShipPartsFactory shipPartsFactory;

  // WhiteshipPartsFactory 또는 WhitePartsProFactory
  public WhiteshipFactory(ShipPartsFactory shipPartsFactory) {
    this.shipPartsFactory = shipPartsFactory;
  }

  /*
  구현클래스의 변경이 발생해도, 해당 코드에 변경은 발생하지 않는다.
   */
  @Override
  public Ship createShip() {
    Ship ship = new Whiteship();
    ship.setAnchor(shipPartsFactory.createAnchor());
    ship.setWheel(shipPartsFactory.createWheel());
    return ship;
  }
}

 

ShipInventory.java
public class ShipInventory {
    public static void main(String[] args) {
        // 원하는 구성품의 버전을 선택
        ShipFactory shipFactory = new WhiteshipFactory(new WhiteshipPartsFactory());
        Ship ship = shipFactory.createShip(); // create

        System.out.println(ship.getAnchor().getClass());
        System.out.println(ship.getWheel().getClass());

        ShipFactory shipProFactory = new WhiteshipFactory(new WhitePartsProFactory());
        Ship shipPro = shipProFactory.createShip();

        System.out.println(shipPro.getAnchor().getClass());
        System.out.println(shipPro.getWheel().getClass());
    }
}

 

 

 

핵심 로직 비교

Before

public class WhiteshipFactory extends ShipFactory {
    @Override
    public Ship createShip() {
        Ship ship = new Whiteship();
        /* 구현클래스 변경시 계속적으로 코드 변경이 발생한다. */
        ship.setAnchor(new WhiteAnchor());
        ship.setWheel(new WhiteWheel());
        return ship;
    }
}

 

After

public class WhiteshipFactory extends DefaultShipFactory {

  private ShipPartsFactory shipPartsFactory;

  // WhiteshipPartsFactory 또는 WhitePartsProFactory
  public WhiteshipFactory(ShipPartsFactory shipPartsFactory) {
    this.shipPartsFactory = shipPartsFactory;
  }

  /*
  구현클래스의 변경이 발생해도, 해당 코드에 변경은 발생하지 않는다.
   */
  @Override
  public Ship createShip() {
    Ship ship = new Whiteship();
    ship.setAnchor(shipPartsFactory.createAnchor());
    ship.setWheel(shipPartsFactory.createWheel());
    return ship;
  }
}
  • WhiteshipFactory 클래스가 직접 Ship 구성품을 선택해야했다면, 이제는 Client가 선택한 구성품에 대한 객체를 코드 수정 없이 생성시킬 수 있게되었다.
  • 여기서 코드 수정이 없다는건, 각 구성품의 버전(default, pro)에 따른 WhiteshipFactory 코드의 수정이 없다는 얘기다.

 

Client

public class ShipInventory {
    public static void main(String[] args) {
        ShipFactory shipFactory = new WhiteshipFactory(new WhiteshipPartsFactory());
        Ship ship = shipFactory.createShip(); // create

        ShipFactory shipProFactory = new WhiteshipFactory(new WhitePartsProFactory());
        Ship shipPro = shipProFactory.createShip();
    }
}
  • Client 에서 원하는 Ship 구성품의 버전을 선택하여 create한다. 만약 Pro 버전 외의 Mega 버전이 추가로 된다면 WhiteshipFacotry 의 코드 수정 없이 추가가 가능하다.

 

 

 

위 Before 에서 봤던 추가 요건을 다시 생각해보자.

1) 추가요건

[추가요건]
구성품 Anchor, Wheel의 Pro 버전이 출시되었습니다. Pro 버전의 Ship을 추가로 생성해주세요.
그 이후엔 Mega 버전으로 추가해주세요.

Client 에서 호출함으로써 Mega 버전의 구성품을 가진 Ship을 생성할 수 있다.

 

 

적용 결과

구성품 버전에 따라 factory 를 분리할 수 있었다.

 

장점

클라이언트 코드(Client)에서 구체적은 클래스의 의존성을 제거했다.

 

단점

단일 책임이라는 원칙을 지키지 못한다.

→ WhiteshipPartsFactory 에서 Anchor, Wheel 에 의존성을 둔다.

 

 

 

팩토리 메서드 패턴과 다른 점이 무엇일까?

이전 포스팅에서 팩토리 메서드 패턴을 복습했다.

https://devfunny.tistory.com/897

 

팩토리 메서드 패턴 (Factory Method Pattern)

수강완료한 강의 복습해보자 (코딩으로 학습하는 GoF의 디자인 패턴) 팩토리 메서드 (Factory Method) 구체적으로 어떤 인스턴스를 만들지는 서브 클래스가 정한다. 다양한 구현체(Product)가 있고, 그

devfunny.tistory.com

내용을 정리해보니 두 디자인패턴이 비슷해보인다. 차이점을 정리해보자.

 

 

팩터리 메서드 패턴 vs 추상 팩터리 패턴

둘다 구체적인 객체 생성 과정을 추상화한 인터페이스를 제공한다.

 

팩터리 메서드 패턴
public class Client {
    public static void main(String[] args) {
        Client client = new Client();
        client.print(new WhiteshipFactory(), "whiteship", "keesun@mail.com");
        client.print(new BlackshipFactory(), "blackship", "keesun@mail.com");
    }

    private void print(ShipFactory shipFactory, String name, String email) {
        System.out.println(shipFactory.orderShip(name, email));
    }
}

 

추상 팩터리 패턴
public class ShipInventory {
    public static void main(String[] args) {
        ShipFactory shipFactory = new WhiteshipFactory(new WhiteshipPartsFactory());
        Ship ship = shipFactory.createShip(); // create

        ShipFactory shipProFactory = new WhiteshipFactory(new WhitePartsProFactory());
        Ship shipPro = shipProFactory.createShip();
    }
}

 

 

‘관점’으로 나눠보자.

팩토리 메서드 패턴

팩토리를 구현하는 방법

public interface ShipFactory {
    // orderShip
    default Ship orderShip(String name, String email) {
        // 어떤일이 일어나는지 default 로 정의해보자.
        validate(name, email);

        prepareFor(name);

        // 하위클래스에 위임
        Ship ship = createShip();

        // notify
        sendEmailTo(email, ship);

        return ship;
    }

    ...
}

 

추상 팩터리 패턴

팩토리를 사용하는 방법

ShipFactory shipFactory = new WhiteshipFactory(new WhiteshipPartsFactory());
Ship ship = shipFactory.createShip(); // create

 

 

‘목적’으로 나눠보자.

팩토리 메서드 패턴

구체적인 객체 생성 과정을 하위 또는 구체적인 클래스로 옮기는 것이 목적

client.print(new WhiteshipFactory(), "whiteship", "keesun@mail.com");

...

public class WhiteshipFactory extends ShipFactory {
    @Override
    public Ship createShip() {
        Ship ship = new Whiteship();
        /* 구현클래스 변경시 계속적으로 코드 변경이 발생한다. */
        ship.setAnchor(new WhiteAnchor());
        ship.setWheel(new WhiteWheel());
        return ship;
    }
}

 

추상 팩터리 패턴

관련있는 여러 객체를 구체적인 클래스에 의존하지 않고 만들 수 있게 해주는 것이 목적

ShipFactory shipFactory = new WhiteshipFactory(new WhiteshipPartsFactory());
Ship ship = shipFactory.createShip(); // create

...

 @Override
  public Ship createShip() {
    Ship ship = new Whiteship();
    ship.setAnchor(shipPartsFactory.createAnchor());
    ship.setWheel(shipPartsFactory.createWheel());
    return ship;
  }

 

 

github : https://github.com/seohaem/java/tree/master/java_GoFdesignpattern

강의 : https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4

 

 

 

반응형

Designed by JB FACTORY