Abstract Factory[Design Pattern]

SnowCat·2023년 2월 17일
0

Design Pattern

목록 보기
3/23
post-thumbnail

추상 팩토리: 관련 객체들의 구상 클래스들을 지정하지 않고도 관련 객체들의 모음을 생성할 수 있도록 하는 생성패턴

문제

  • 가구 판매장을 위한 프로그램을 만들고 있다. 코드에는 다음 클래스들이 있다
    • 제품군: 의자, 소파 커피 테이블 등등..
    • 제품군의 여러 변형: 각각은 현대식, 빅토리안, 아르데코 양식 등을 가질 수 있음
    • 새로운 개별 가구 객체를 생성할 때 새로운 객체는 같은 패밀리 내에 있는 다른 가구 객체들과 일치하는 스타일을 가져야 함

해결책

  1. 각 제품군에 해당되는 개별적인 인터페이스를 명시적으로 선언하고, 제품의 모든 변형은 이 인터페이스를 따르게 해야 함

  2. 제품 패밀리 내의 모든 개별 제품들의 생성 메서드들이 목록화 되어 있는 인터페이스인 추상 팩토리 패턴 선언

  3. 추상 팩토리 인터페이스를 기반으로 각 특성의 제품을 생성하는 별도의 팩토리 클래스 생성

  4. 클라이언트 코드는 자신에 해당하는 추상 인터페이스를 통해 제품들 모두와 작동해야 함
    프로그램은 초기화 단계에서 구상 팩토리 객체를 생성하고, 환경, 구성 설정에 따라 팩토리 유형을 선택함

구조

// 생성할 모든 객체들을 담은 추상 패토리 인터페이스
interface AbstractFactory {
    createProductA(): AbstractProductA;

    createProductB(): AbstractProductB;
}

// 구상 팩토리에서 추상 팩토리를 바탕으로 각각의 특성에 맞는 객체 생성
class ConcreteFactory1 implements AbstractFactory {
    public createProductA(): AbstractProductA {
        return new ConcreteProductA1();
    }

    public createProductB(): AbstractProductB {
        return new ConcreteProductB1();
    }
}

class ConcreteFactory2 implements AbstractFactory {
    public createProductA(): AbstractProductA {
        return new ConcreteProductA2();
    }

    public createProductB(): AbstractProductB {
        return new ConcreteProductB2();
    }
}

// 제품군의 모든 변형 객체를 생성해주는 객체 생성 인터페이스 구현
// 여기서는 A제품을 생성하는 인터페이스
interface AbstractProductA {
    usefulFunctionA(): string;
}

// 구체적인 클래스 구현, 각각의 객체는 객체의 특성에 맞추어 구현됨
class ConcreteProductA1 implements AbstractProductA {
    public usefulFunctionA(): string {
        return 'The result of the product A1.';
    }
}

class ConcreteProductA2 implements AbstractProductA {
    public usefulFunctionA(): string {
        return 'The result of the product A2.';
    }
}

//한번더 반복
interface AbstractProductB {
  
    usefulFunctionB(): string;
    anotherUsefulFunctionB(collaborator: AbstractProductA): string;
}

// A와 인터페이스, 객체 생성 방법은 동일하나 다른 특성을 갖는 객체 생성
class ConcreteProductB1 implements AbstractProductB {

    public usefulFunctionB(): string {
        return 'The result of the product B1.';
    }
    public anotherUsefulFunctionB(collaborator: AbstractProductA): string {
        const result = collaborator.usefulFunctionA();
        return `The result of the B1 collaborating with the (${result})`;
    }
}

class ConcreteProductB2 implements AbstractProductB {

    public usefulFunctionB(): string {
        return 'The result of the product B2.';
    }
    public anotherUsefulFunctionB(collaborator: AbstractProductA): string {
        const result = collaborator.usefulFunctionA();
        return `The result of the B2 collaborating with the (${result})`;
    }
}

// 클라이언트들은 추상 인터페이스를 통해 원하는 특성의 객체만를 사용할 수 있게 됨
function clientCode(factory: AbstractFactory) {
    const productA = factory.createProductA();
    const productB = factory.createProductB();

    console.log(productB.usefulFunctionB());
    console.log(productB.anotherUsefulFunctionB(productA));
}

/*
Client: Testing client code with the first factory type...
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)
*/
console.log('Client: Testing client code with the first factory type...');
clientCode(new ConcreteFactory1());
console.log('');

/*
Client: Testing the same client code with the second factory type...
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)
*/
console.log('Client: Testing the same client code with the second factory type...');
clientCode(new ConcreteFactory2());

적용

  • 관련된 객체들의 다양한 패밀리들과 작동해야 하지만, 구체적인 클래스들에 의존하고 싶지 않을 때 사용
    • 클래스들을 나중에 추가하게 될 수도 있음
    • 의도하지 않은 잘못된 객체를 생성할 위험을 사전에 방지해줌
  • 코드에 클래스가 있고, 클래스의 팩토리 메서드들의 집합의 기본책임지 뚜렷하지 않을 때 사용
    • 클래스가 여러 유형을 상대할 경우 팩토리 클래스를 분리하거나 추상 팩토리 구현으로 변경하는 것이 좋음

구현 방법

  1. 제품군(의자, 테이블 등) - 제품군에서 파생된 변형품(동그란 의자, 네모난 테이블 등)을 맵핑
  2. 모든 제품 변형들에 대한 추상 제품 인터페이스 선언 (의자 인터페이스, 테이블 인터페이스)
  3. 모든 구체적 제품 클래스들에 대해 추상 제품 인터페이스를 따르게 함
  4. 추상 팩토리 인터페이스를 모든 추상 제품들에 대한 생성 메서드들의 집합과 함께 선언
  5. 각각의 변형품 집합에 대해 하나의 구상 팩토리 클래스 집합 구현
  6. 앱의 어딘가에 팩토리 초기화 코드를 작성하고 제품을 생성하는 모든 클래스에 전달
    초기화 코드는 앱 설정, 환경에 따라 구상 팩토리 클래스 중 하나를 결정해야 함
  7. 모든 제품 생성자에 대한 직접 호출을 찾고, 이들을 팩토리 객체에 대한 적절한 생성 메서드에 의한 호출로 교체

장단점

  • 팩토리에서 생성되는 객체들의 상호호환 보장
  • 실제 객체와 클라이언트 코드 사이에 결합을 약하게 해줌으로써 개방/폐쇄 원칙 준수
  • 제품 생성코드를 한 곳으로 몰아 단일 책임 원칙 준수
  • 패턴과 함께 새로운 인터페이스, 클래스들이 많이 도입되기 때문에 코드가 복잡해질 수 있음

출처:
https://refactoring.guru/ko/design-patterns/abstract-factory

profile
냐아아아아아아아아앙

0개의 댓글