ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [디자인패턴]팩토리 패턴
    IT 발자취.../디자인패턴 2018. 12. 9. 03:14

    디자인패턴을 공부하기 전 디자인패턴 뿐만 아니라
    자주 나오는 용어지만 제대로 알지 못한 용어를 정리하고자합니다.

    Concrete Class(구상클래스) vs Abstract Class (추상클래스)
    추상 클래스는 객체 지향을 공부하며 많이 공부했을 것이라 생각합니다.
    디자인 패턴을 공부하며 많이 나왔던 Concrete Class는
    구체화된 클래스? 라고 생각하면 될 것같습니다.
    즉, 모든 오퍼레이션의 구현을 제공하는 클래스 정도로
    알고 있으면 될 것 같습니다. :)

    [팩토리패턴]
    1. 정의
    - 모든 팩토리 패턴에는 객체 생성을 캡슐화 한다.
    - 팩토리 메서드 패턴과 추상 팩토리 패턴이 있다.
     - 팩토리 메서드 패턴 : 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정한다.
    - 추상 팩토리 패턴 : 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수  있다. 추상 팩토리 패턴에는 팩토리 메서드 패턴이 포함될 수 있다.
    - 디자인 원칙 중 '추상화된 것에 의존하도록 만들어라. 구상클래스에 의존하지 않도록 만든다.'에 기인한 패턴.

    팩토리 패턴의 핵식은 클래스의 인스턴스를
    만드는 것을 서브클래스에서
    결정하도록 한다 입니다.

    즉, new 키워드를 사용하는 부분을 서브클래스에
    위임함으로서 객체 생성을 캡슐화 하고
    구상클래스에 대한 의존성이 줄어든다는 이점을 얻을 수 있습니다.

    특히 구상클래스에 대한 의존성이 줄어드는 것은
    의존성 뒤집기 원칙(Dependency Inversion Principle : DI)에 
    기인하는데, DI는 자바진영에서 널리 쓰이고 있는 Spring Framework의
    핵심 개념 중 하나 입니다. 
    싱글톤패턴과 더불어 가장 유명하고 널리 쓰이는
    디자인 패턴 중 하나라고 할 수 있겠습니다.

    <<팩토리 메서드 패턴 개념도>>

    <<추상 팩토리 패턴 개념도>>

    2. 예시

    팩토리패턴 예시를 어느 카페
    커피 종류들을 이용해 예시를 들어보겠습니다.

    원하는 커피를 주문 받으면 커피를 제공해야합니다.

    static public void main(String[] args){ Beverage beverage = null; String type="Espresso"; switch(type) { case("DarkRoast") : beverage = new DarkRoast(); break; case("Espresso") : beverage = new Espresso(); break; case("HouseBlend") : beverage = new HouseBlend(); break; default: beverage = new Americano(); break; } System.out.println(beverage.getName()); /** * 결과 * Espresso */ }

    음류 종류에 따라 음류를 생성할 수 있습니다.
    하지만 위와 같이 코드를 작성하게 되면
    변경이나 확장할 요소가 생길 시 매번 코드를 추가, 제거
    해주어야 한다는 문제가 발생합니다.

    위 문제는 객체 인스턴스를 생성하는(new 키워드를 사용하는) 부분을 별도의
    인터페이스로 분리하면 해결할 수 있습니다.
    이렇게 생성자를 별도의 인터페이스로
    분리하여 객체를 만들어내는 공장(factory)로 이용하는 것이
    팩토리 메서드 패턴입니다.

    2.1 예 : 팩토리 메서드 패턴

    위 코드에서 인스턴스를 생성하는 부분을 인터페이스로 분리해보겠습니다.

    <<팩토리 메서드 패턴>>

    UML로 그려보면 이런 모습이 됩니다. 각각의 Beverage 클래스들은 Beverage 추상 클래스를 상성받아 getName 메서드를 구현하고, BeverageFactory 추상클래스를 상속받은 TypeBeverageFactory에서는 type별로 Beverage 인스턴슬르 생성하도록 하는 createBeverage 메서드를 구현하도록 합니다. 먼저 각 Beverage 클래스 예시 코드입니다.

    //Beverage Abstract class (Component) public abstract class Beverage { public abstract String getName(); } //Beverage Concrete Class public class Espresso extends Beverage { public Espresso() {}; public String getName() { return "Espresso"; } }
    //Beverage Factory Abstract Class public abstract class BeverageFactory { public abstract Beverage createBeverage(String type); } //Beverage Factory Concrete Class public class TypeBeverageFactory extends BeverageFactory{ private TypeBeverageFactory() {} @Override public Beverage createBeverage(String type) { Beverage beverage = null; switch(type) { case("DarkRoast") : beverage = new DarkRoast(); break; case("Espresso") : beverage = new Espresso(); break; case("HouseBlend") : beverage = new HouseBlend(); break; default: beverage = new Americano(); break; } return beverage; } }
    static public void main(String[] args){ TypeBeverageFactory typeBeverageFactory = new TypeBeverageFactory(); Beverage beverage1 = typeBeverageFactory.createSuit("DarkRoast"); Beverage beverage2 = typeBeverageFactory.createSuit("Espresso"); Beverage beverage3 = typeBeverageFactory.createSuit(""); System.out.println(beverage1.getName()); System.out.println(beverage2.getName()); System.out.println(beverage3.getName()); /** * 결과 * DarkRoast * Espresso * Americano */ }

    인스턴스 생성을 서브클래스로 위임한 결과입니다. 최종 메인 메서드에서는 new 키워드를 사용하여 인스턴스를 생성한 부분이 없는 것을 확인할 수 있습니다. 이를 통해 메인 프로그램에서는 어떤 객체가 생성되었는지 신경 쓸 필요없이 반환된 객체만 사용하면 되고 Beverage클래스에서 변경이 발생해도 메인 프로그램이 변경되는 것은 최소화할 수 있습니다.

    2.2 예시 : 추상 팩토리 패턴
    추상 팩토리 패턴에 대한 예시입니다. 위의 정의에서 추상 팩토리 패턴은 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있다고 했습니다. 즉, 연관된 서브 클래스를 그룹화할 수 있고 이것은 이 그룹을 자유롭게 교체할 수 있는 패턴 이라고 할 수 있습니다.

    BeverageAbstractFactory 인터페이스를 작성하고 이를 상속받아 각 커피를 생성하는 팩토리 클래스를 구성합니다. BeverageFactory에서는 이 팩토리를 파라미터로 받아 최종적으로 생성된 Beverage 객체를 반환하게 됩니다.

    //Abstract Factory Interface public interface BeverageAbstractFactory { public Beverage createBeverage(); } //DarkBlend factory class public class DarkBlendFactory implements BeverageAbstractFactory{ @Override public Beverage createBeverage() { return new DarkBlend(); } } //Concrete Class public class BeverageFactory{ static public Beverage getBeverage(BeverageAbstractFactory beverageAbstractFactory) { return beverageAbstractFactory.createBeverage(); } }
    static public void main(String[] args){ Beverage beverage1 = BeverageFactory.getBeverage(new DarkBlendFactory()); Beverage beverage2 = BeverageFactory.getBeverage(new HouseBlendFactory()); Beverage beverage3 = BeverageFactory.getBeverage(new AmericanoFactory()); System.out.println(beverage1.getName()); System.out.println(beverage2.getName()); System.out.println(beverage3.getName()); /** * 결과 * DarkBlend * HouseBledn * Americano */ }

    위 결과 코드와 같이 커피별 팩토리 클래스를 파라미터로 넘겨 각 커피 객체를 반환 받아 사용할 수 있습니다. 이렇게 되면 팩토리 클래스 교체 만으로 조금 더 유연하게 기능을 수정, 확장에 대처할 수 있게 됩니다. 코드상으로 if-else구문을 제거하여 조금 더 깔끔하게 코드를 구성할 수 있습니다.

    'IT 발자취... > 디자인패턴' 카테고리의 다른 글

    [디자인패턴]싱글턴 패턴  (0) 2018.12.09
    [디자인패턴] 데코레이터 패턴  (0) 2018.12.09

    댓글

Designed by Gintire