1. 소개

소프트웨어 엔지니어링에서 디자인 패턴은 소프트웨어 디자인에서 가장 일반적으로 발생하는 문제에 대한 확립된 솔루션을 설명합니다. 숙련된 소프트웨어 개발자가 시행착오를 거쳐 오랜 기간 동안 발전한 모범 사례를 나타냅니다.

디자인 패턴 은 1994년 Erich Gamma, John Vlissides, Ralph Johnson 및 Richard Helm(Gang of Four 또는 GoF라고도 함)이 Design Patterns: Elements of Reusable Object-Oriented Software를 출판한 후 인기를 얻었습니다.

이 기사에서는 창작 디자인 패턴과 그 유형을 살펴보겠습니다. 또한 몇 가지 코드 샘플을 살펴보고 이러한 패턴이 우리 디자인에 적합한 상황에 대해 논의할 것입니다.

2. 창조적인 디자인 패턴

생성 디자인 패턴은 개체가 생성되는 방식과 관련이 있습니다. 제어된 방식으로 개체를 생성하여 복잡성과 불안정성을 줄입니다.

new 연산자는 응용 프로그램 전체에 개체를 흩뿌리기 때문에 종종 유해한 것으로 간주됩니다 . 시간이 지남에 따라 클래스가 밀접하게 결합되기 때문에 구현을 변경하는 것이 어려울 수 있습니다.

생성 디자인 패턴은 실제 초기화 프로세스에서 클라이언트를 완전히 분리하여 이 문제를 해결합니다.

이 기사에서는 창조적인 디자인 패턴의 네 가지 유형에 대해 설명합니다.

  1. Singleton – 응용 프로그램 전체에서 개체의 인스턴스가 최대 하나만 존재하도록 합니다.
  2. Factory Method – 생성할 정확한 개체를 지정하지 않고 여러 관련 클래스의 개체를 생성합니다.
  3. 추상 팩토리 - 관련 종속 객체의 패밀리를 생성합니다.
  4. 빌더 단계별 접근 방식을 사용하여 복잡한 개체를 구성합니다.

이제 이러한 각 패턴에 대해 자세히 설명하겠습니다.

3. 싱글톤 디자인 패턴

싱글톤 디자인 패턴 은 객체의 인스턴스가 Java Virtual Machine 전체에 하나만 존재하도록 하여 특정 클래스 객체의 초기화를 계속 확인하는 것을 목표로 합니다 .

Singleton 클래스는 객체에 대한 하나의 고유한 전역 액세스 지점을 제공하므로 액세스 지점에 대한 각 후속 호출은 해당 특정 객체만 반환합니다.

3.1. 싱글톤 패턴 예제

Singleton 패턴은 GoF에 의해 도입되었지만 원래 구현은 다중 스레드 시나리오에서 문제가 있는 것으로 알려져 있습니다.

따라서 여기에서는 정적 내부 클래스를 사용하는 보다 최적의 접근 방식을 따를 것입니다.

public class Singleton  {    
    private Singleton() {}
    
    private static class SingletonHolder {    
        public static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {    
        return SingletonHolder.instance;    
    }
}

여기에서는 Singleton 클래스 의 인스턴스를 보유하는 정적 내부 클래스를 만들었습니다 . 외부 클래스가 로드될 때가 아니라 누군가 getInstance() 메서드를 호출할 때만 인스턴스를 생성합니다 .

이것은 동기화가 필요하지 않고 스레드로부터 안전하며 지연 초기화를 시행하고 상대적으로 상용구가 적기 때문에 Singleton 클래스에 널리 사용되는 접근 방식입니다.

또한 생성자에는 개인용 액세스 수정자가 있습니다. 공용 생성자는 누구나 액세스하고 새 인스턴스를 생성할 수 있음을 의미 하므로 Singleton을 생성하기 위한 요구 사항입니다 .

이것은 원래 GoF 구현이 아닙니다. 원본 버전은 Java의 싱글톤에 대한 링크된 Baeldung 기사를 참조하십시오.

3.2. 싱글톤 디자인 패턴을 사용하는 경우

  • 생성 비용이 많이 드는 리소스의 경우(예: 데이터베이스 연결 개체)
  • 모든 로거를 싱글톤으로 유지하여 성능을 높이는 것이 좋습니다.
  • 애플리케이션의 구성 설정에 대한 액세스를 제공하는 클래스
  • 공유 모드에서 액세스되는 리소스를 포함하는 클래스

4. 팩토리 메소드 디자인 패턴

Factory Design Pattern 또는 Factory Method Design Pattern은 Java에서 가장 많이 사용되는 디자인 패턴 중 하나입니다.

GoF에 따르면 이 패턴은 "객체를 생성하기 위한 인터페이스를 정의하지만 하위 클래스가 인스턴스화할 클래스를 결정하도록 합니다. Factory 메서드를 사용하면 클래스가 인스턴스화를 하위 클래스로 연기할 수 있습니다.”

이 패턴은 가상 생성자 유형을 생성하여 클라이언트에서 특정 팩터리 클래스로 클래스를 초기화하는 책임을 Delegation합니다.

이를 달성하기 위해 실제 구현 세부 정보를 숨기고 개체를 제공하는 팩터리에 의존합니다. 생성된 개체는 공통 인터페이스를 사용하여 액세스됩니다.

4.1. 팩토리 메소드 디자인 패턴 예제

이 예제에서는 여러 구체적인 클래스에 의해 구현될 Polygon 인터페이스를 만듭니다 . PolygonFactory이 패밀리에서 객체를 가져오는 데 사용됩니다.

팩토리 메소드 디자인 패턴

먼저 Polygon 인터페이스를 생성해 보겠습니다 .

public interface Polygon {
    String getType();
}

다음으로 이 인터페이스를 구현하고 Polygon 유형 의 개체를 반환하는 Square , Triangle 등과 같은 몇 가지 구현을 만듭니다 .

이제 측면의 수를 인수로 사용하고 이 인터페이스의 적절한 구현을 반환하는 팩터리를 만들 수 있습니다.

public class PolygonFactory {
    public Polygon getPolygon(int numberOfSides) {
        if(numberOfSides == 3) {
            return new Triangle();
        }
        if(numberOfSides == 4) {
            return new Square();
        }
        if(numberOfSides == 5) {
            return new Pentagon();
        }
        if(numberOfSides == 7) {
            return new Heptagon();
        }
        else if(numberOfSides == 8) {
            return new Octagon();
        }
        return null;
    }
}

객체를 직접 초기화하지 않고도 클라이언트가 적절한 Polygon 을 제공하기 위해 이 팩토리에 어떻게 의존할 수 있는지 주목하십시오.

4.2. 팩토리 메소드 디자인 패턴을 사용하는 경우

  • 인터페이스 또는 추상 클래스의 구현이 자주 변경될 것으로 예상되는 경우
  • 현재 구현이 새로운 변화를 편안하게 수용할 수 없는 경우
  • 초기화 프로세스가 비교적 간단하고 생성자에 소수의 매개변수만 필요한 경우

5. 추상적인 Factory 디자인 패턴

이전 섹션에서는 Factory Method 디자인 패턴을 사용하여 단일 패밀리와 관련된 개체를 만드는 방법을 살펴보았습니다.

대조적으로 추상 팩토리 디자인 패턴은 관련되거나 종속된 개체의 패밀리를 만드는 데 사용됩니다. 때로는 Factory 중의 Factory이라고도 합니다.

자세한 설명은 Abstract Factory 예제을 확인하세요.

6. 빌더 디자인 패턴

빌더 디자인 패턴은 비교적 복잡한 객체의 구성을 처리하도록 설계된 또 다른 생성 패턴입니다.

개체 생성의 복잡성이 증가하면 빌더 패턴은 다른 개체(빌더)를 사용하여 개체를 구성함으로써 인스턴스화 프로세스를 분리할 수 있습니다.

그런 다음 이 빌더를 사용하여 간단한 단계별 접근 방식을 사용하여 다른 많은 유사한 표현을 만들 수 있습니다.

6.1. 빌더 패턴 예제

GoF에서 소개한 원래 빌더 디자인 패턴은 추상화에 중점을 두고 복잡한 객체를 처리할 때 매우 좋지만 디자인이 약간 복잡합니다.

Joshua Bloch는 그의 저서 Effective Java에서 깨끗하고 가독성이 높으며( 유창한 디자인을 사용하기 때문에) 클라이언트 관점에서 사용하기 쉬운 향상된 버전의 빌더 패턴을 소개했습니다. 이 예에서는 해당 버전에 대해 설명합니다.

이 예제 에는 빌더를 정적 내부 클래스로 포함하는 BankAccount라는 하나의 클래스만 있습니다 .

public class BankAccount {
    
    private String name;
    private String accountNumber;
    private String email;
    private boolean newsletter;

    // constructors/getters
    
    public static class BankAccountBuilder {
        // builder code
    }
}

외부 객체가 필드에 직접 액세스하는 것을 원하지 않기 때문에 필드의 모든 액세스 한정자는 비공개로 선언됩니다 .

생성자도 비공개 이므로 이 클래스에 할당된 빌더만 액세스할 수 있습니다. 생성자에 설정된 모든 속성은 우리가 인수로 제공하는 빌더 개체에서 추출됩니다.

정적 내부 클래스 에 BankAccountBuilder를 정의했습니다 .

public static class BankAccountBuilder {
    
    private String name;
    private String accountNumber;
    private String email;
    private boolean newsletter;
    
    public BankAccountBuilder(String name, String accountNumber) {
        this.name = name;
        this.accountNumber = accountNumber;
    }

    public BankAccountBuilder withEmail(String email) {
        this.email = email;
        return this;
    }

    public BankAccountBuilder wantNewsletter(boolean newsletter) {
        this.newsletter = newsletter;
        return this;
    }
    
    public BankAccount build() {
        return new BankAccount(this);
    }
}

외부 클래스에 포함된 것과 동일한 필드 집합을 선언했습니다. 모든 필수 필드는 내부 클래스의 생성자에 대한 인수로 필요하며 나머지 선택적 필드는 setter 메서드를 사용하여 지정할 수 있습니다.

이 구현은 또한 setter 메서드가 빌더 개체를 반환하도록 하여 유창한 디자인 접근 방식을 지원합니다.

마지막으로 빌드 메서드는 외부 클래스의 전용 생성자를 호출하고 자신을 인수로 전달합니다. 반환된 BankAccount는 BankAccountBuilder 에 의해 설정된 매개변수로 인스턴스화됩니다 .

작동 중인 빌더 패턴의 간단한 예를 살펴보겠습니다.

BankAccount newAccount = new BankAccount
  .BankAccountBuilder("Jon", "22738022275")
  .withEmail("jon@example.com")
  .wantNewsletter(true)
  .build();

6.2. 빌더 패턴을 사용하는 경우

  1. 객체 생성과 관련된 프로세스가 필수 및 선택적 매개 변수가 많아 매우 복잡한 경우
  2. 생성자 매개변수의 수가 증가하면 생성자 List이 많아지는 경우
  3. 클라이언트가 구성된 객체에 대해 다른 표현을 기대하는 경우

7. 결론

이 기사에서는 Java의 생성 디자인 패턴에 대해 배웠습니다. 또한 Singleton, Factory Method, Abstract Factory 및 Builder Pattern의 네 가지 유형, 장점, 예제 및 언제 사용해야 하는지에 대해서도 논의했습니다.

항상 그렇듯이 전체 코드 스니펫은 GitHub 에서 사용할 수 있습니다 .

Generic footer banner