창조적 패턴은 유명한 네 명의 갱에서 처음으로 묘사되었습니다. 이 책은 각 패턴을 전용 장에 제시하고 각각에 대한 엄격한 구조를 따릅니다 : 의도, 동기, 적용 가능성, 구조, 참가자, 협력, 결과, 구현, 샘플 코드, 알려진 사용 및 관련 패턴.The intent pattern presents a concise goal of the pattern, while the applicability tells when you should use it. 디자인 패턴 예를 들어, 여기에 그것을위한 추출물이 있습니다. 패턴 : 건축가 시도 : 동일한 건설 과정이 다른 표현을 만들 수 있도록 복잡한 개체의 건설을 그 표현에서 분리한다. 적용 가능성 : Builder 패턴을 사용할 때 the algorithm for creating a complex object should be independent of the Parts that make up the object and how they're assembled. the construction process must allow different representations for the object that's constructed. : Intent 동일한 건설 과정이 다른 표현을 만들 수 있도록 복잡한 개체의 건설을 그 표현에서 분리한다. : Applicability Builder 패턴을 사용할 때 복잡한 개체를 만드는 알고리즘은 개체를 구성하는 부품과 그들이 어떻게 조립되었는지에 독립되어야 한다. 건설 과정은 건설되는 개체에 대한 다른 표현을 허용해야 한다. GoF (Gang of Four)는 객체 지향 프로그래밍의 영역에서 기초적이었으며 Java와 같은 널리 퍼진 프로그래밍 언어를 포함하여 프로그래밍 언어의 설계에 영향을 미쳤습니다. 내가 다시 엔지니어링 위치에있을 때, 나는 유지 보수에 관해서 많은 개선 잠재력을 가지고있는 새로 작성 된 자바 코드를 만난다.그것은 작동하지만, 나는 엔지니어가 원래 저자의 미래 자신과 나를 포함하여 그것을 업데이트해야한다고 상상하고, 나는 그들을 도울 수 있다고 확신합니다.이 주, 나는 유지 보수를 개선하기 위해 창조적 인 패턴을 사용하여 코드를 재현했습니다.이 포스트에서, 나는 내가 직면 한 문제를 설명하고 패턴이 나를 어떻게 도왔는지 언급하고 싶습니다. 동일한 유형의 많은 매개 변수를 가진 건설자. 많은 String 매개 변수를 가진 constructor를 상상해보자: public License ( String id, String licenseeName, String licenseId, String environment, LocalDateTime generatedAt ) 이 생성기를 호출할 때, 호출자는 의도치 않게 매개 변수 명령을 전환할 수 있습니다: var license = new License("My license", "XXX-123", "Customer", "User-acceptance tests", new LocalDateTime()); 오프스, 나는 라이센서 이름과 라이센스 ID를 변경했습니다.IDE는 여기에 도움이 될 수 있지만 다른 방법이 있습니다. 종류 Wrappers 순수 OOP의 지지자들은 한 번도 직접 문자열을 사용해서는 안된다는 것을 기꺼이 지적할 것입니다.Instead, one should wrap each parameter in a dedicated type, 그리고 java : 에이즈 G record public record Id(String id) { ... } public record LicenseeName(String licenseeName) { ... } public record LicenseeId(String licenseId) { ... } public record Environment(String environment) { ... } public record GeneratedAt(LocalDateTime generatedAt) { ... } 이제 우리는 실수를 할 수 없습니다 : var id = new Id("My license"); var licenseeName = new LicenseeName("Customer"); var licenseId = new LicenseeId("XXX-123"); var environment = new Environment("User-acceptance tests"); var generatedAt = new LocalDateTime(); var license = new License(id, licenseId, licenseName, environment, generatedAt); //1 Compile-time error 이 접근 방식은 확실히 유지 보수성을 향상시킵니다만, 포장기는 메모리 크기를 증가시킵니다.The exact increase depends on the JDK implementation, but for a single type, it is around 5 times larger. Kotlin은 제공함으로써 그것을 바람으로 만듭니다. : 포장은 컴파일 타임 체크이지만 바이트코드는 다음과 같은 제한을 가진 포장 타입을 가리킨다: the wrapping is a compile-time check, but the bytecode points to the wrapped type with the following limitation: Inline Value 클래스 인라인 클래스는 기본 구축자에 초기화된 단일 속성을 가져야 합니다.An inline class must have a single property initialized in the primary constructor. An inline class must have a single property initialized in the primary constructor Parameter 이름 Java는 위치 매개 변수로만 메서드 호출을 제공하지만, 다른 언어들은 , Python, Kotlin, and Rust, also offer named parameters. 에이즈 G 위의 클래스를 반영하는 Kotlin constructor는 다음과 같습니다: class License ( val id: String, val licenseeName: String, val licenseId: String, val environment: String, val generatedAt: LocalDateTime ) 파라미터를 이름으로 제작자를 호출하여 실수의 위험을 줄일 수 있습니다.You can call the constructor by naming the parameters, thus reducing the risks of making a mistake: val license = License( id = "My license", licenseeName = "Customer", licenseId = "XXX-123", environment = "User-acceptance tests", generatedAt = LocalDateTime() ) 이 패턴 건축가 이 패턴은 GoF에 설명된 사용 사례의 일부가 아니더라도 또 다른 실현 가능한 접근법입니다. 건축가 여기 코드가 있습니다: public class License { private final String id; private final String licenseeName; private final String licenseId; private final String environment; private final LocalDateTime generatedAt; private License ( //1 String id, String licenseeName, String licenseId, String environment, LocalDateTime generatedAt ) { ... } public static LicenseBuilder builder() { //2 return new LicenseBuilder(); } public static class LicenseBuilder { private String id; //3 private String licenseeName; //3 private String licenseId; //3 private String environment; //3 private LocalDateTime generatedAt; //3 private LicenseBuilder() {} //1 public LicenseBuilder withId(String id) { //4 this.id = id; //5 return this; //4 } // Other `withXXX` methods public License build() { //6 return new License( id, licenseeName, licenseId, environment, generatedAt ); } } } Direct Object Instantiation을 방지하기 새로운 건축가를 만들기 Builder 필드는 객체의 필드를 모방합니다.The builder fields mimic the object's fields. 각 방법은 Builder Object 자체를 반환합니다. Attribute를 지정하라 완전한 개체를 반환 이제는 건축가를 이렇게 부를 수 있습니다 : val license = License.builder() .withId("My license") .withLicenseName("Customer") .withLicenseId("XXX-123") .withEnvironment("User-acceptance tests") .withGeneratedAt(new LocalDateTime()) 빌더 코드를 만드는 것은 고통 스럽습니다 (AI를 사용하지 않는 한), 그러나 더 나은 읽기 가능성을 허용합니다.또한, 모든 메서드 호출에 대한 검증을 추가하여 건설 중인 개체가 유효하도록 할 수 있습니다. . Faceted 건축가 요약 Approach Pros Cons Type wrappers Object-Oriented Programming - More verbose- Can be memory-heavy depending on the language Named parameters Easy Not available in Java Builder pattern Verbose - Allows creating complex objects- Allows validating 종류 Wrappers Object-Oriented 프로그래밍 - 더 verbose- 언어에 따라 메모리 무거운 수 있습니다. Parameter 이름 쉬운 Java에서 사용할 수 없습니다. 건축 패턴 거짓말 - 복잡한 개체를 만들 수 있습니다 - Validate 제외 사항을 던지는 제작자들 같은 코드베이스에서, 나는 다음 코드를 발견했다: public Stuff(UuidService uuidService, FallbackUuidService fallbackUuidService) { try { uuid = uuidService.getUuid(); } catch(CannotGetUuidException e) { try { uuid = fallbackUuidService.getUuid(); } catch(CannotGetUuidException e1) { uuid = "UUID can be fetched"; } } } With a modicum of experience, you can notice what's wrong in the above snippet. If both services fail, UUID가 아닌 문자열로 초기화됩니다. UUID에 의존하는 모든 코드는 UUUID가 아닌 값을 다루어야 합니다. 빠르게 실패해야 합니다. uuid public Stuff(UuidService uuidService, FallbackUuidService fallbackUuidService) { try { uuid = uuidService.getUuid(); } catch(CannotGetUuidException e) { try { uuid = fallbackUuidService.getUuid(); } catch(CannotGetUuidException e1) { throw new RuntimeException(e1); } } } 이제, 모든 개체에는 유효한 UUID가 있습니다.그러나 건축기 내에서 예외를 던지는 것은 잠재적 인 문제가 있습니다: Stuff 리소스 유출: 만약 제작자가 리소스(예: 파일, 소켓)를 할당하고 예외를 던지면, 그 리소스가 풀리지 않을 수 있습니다.It can be mitigated by being careful and using try-catch-final blocks. 유산: 슈퍼 클래스 빌더가 예외를 던지면 하위 클래스 빌더가 실행되지 않습니다. 검증된 예외: Constructors에서 검증된 예외를 사용하는 것은 불가능하며, Runtime 예외만 사용할 수 있습니다. 이러한 이유로, 나는 예외가 건설자에서 그 자리가 없다고 생각하고 나는 그들을 피한다. 첫 번째 섹션에서 설명 된 패턴, 그러나 언급 한 바와 같이, 그것은 많은 코드; 나는 그것이 필요하다고 생각하지 않는다. . 건축가 공장 방법 시도 Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. 적용성 공장 방법 패턴을 사용할 때: a class can't anticipate the class of objects it must create. a class wants its subclasses to specify the objects it creates. 클래스는 여러 헬퍼 하위 클래스 중 하나에 책임을 전달하고, 어느 헬퍼 하위 클래스가 대리인 지식을 위치화하려고 합니다.class delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate. Intent 개체를 만들기 위한 인터페이스를 정의하지만, 하위 클래스가 어떤 클래스를 인스턴티화할지 결정할 수 있도록 합니다.Factory Method allows a class to postpone instantiation to subclasses. Applicability 공장 방법 패턴을 사용할 때: a class can't anticipate the class of objects it must create. a class wants its subclasses to specify the objects it creates. 클래스는 여러 헬퍼 하위 클래스 중 하나에 책임을 전달하고, 어느 헬퍼 하위 클래스가 대리인 지식을 위치화하려고 합니다.class delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate. 이 경우, 우리는 다른 이유로 그것을 사용한다는 점에 유의하십시오.여기에 업데이트 된 코드: public class Stuff { private final UUID uuid; private Stuff(UUID uuid) { //1 this.uuid = uuid; } public static Stuff create(UuidService uuidService, FallbackUuidService fallbackUuidService) throws CannotGetUuidException { try { return new Stuff(uuidService.getUuid()); } catch(CannotGetUuidException e) { return new Stuff(fallbackUuidService.getUuid()); //2 } } } External Instantiation을 방지하기 실패하면 새로운 CannotGetUuidException을 던집니다. 위의 하나는 이렇게 부른다: var stuff = Stuff.create(uuidService, fallbackUuidService); //1 CannotGetUuidException을 잡아야합니다. 이 시점에서 호출이 성공하면 객체가 완전히 초기화되었는지 확인할 수 있습니다.If it does not, no object is created. 결론 이 게시물에서 나는 책에 나열되지 않은 GoF의 창조적 인 패턴의 두 가지 사용법을 설명했습니다 : 유지 보수를 개선하고 개체가 완전히 초기화되도록 보장합니다.Knowing your classics allows you to use them in cases that fit, but weren't initially listed for. To go further: GoF 디자인 패턴 A Dive Into the Builder 패턴 빌더 패턴은 유한한 상태 기계입니다! 원래에 게시 된 A Java Geek on 8월 31st, 2025 자바 Geek