English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
이 글은 Android 프로그래밍 설계 패턴인 Builder 모드의 예제를 설명합니다. 여러분과 공유하고, 구체적으로 다음과 같습니다:
1. 소개
Builder 모드는 복잡한 객체를 단계별로 생성하는 생성형 패턴으로, 사용자가 내부 구성 세부 사항을 알지 않더라도, 객체의 구성 과정을 더 세세하게 제어할 수 있도록 합니다. 이 패턴은 복잡한 객체의 구성 과정과 부품을 결합해제하여, 구성 과정과 부품의 표현을 분리하는 데 목적이 있습니다.
복잡한 객체는 많은 많은 구성 요소를 가지고 있기 때문에, 예를 들어 차는 바퀴, 핸들, 엔진 등이 있으며, 여러 가지 작은 부품도 있습니다. 이러한 부품을 어떻게 차로 조립할 것인가, 이 조립 과정은 매우 길고 복잡합니다. 이러한 경우, 구성 과정에서 외부에 구현 세부 사항을 숨기기 위해 Builder 모드를 사용하여 부품과 조립 과정을 분리하여, 구성 과정과 부품이 자유롭게 확장되고, 두 가지 사이의 결합도가 최소화됩니다.
2. 정의
복잡한 객체의 구성과 표현을 분리하여, 동일한 구성 과정이 다른 표현을 생성할 수 있도록 합니다.
3. 사용 시나리오
(1같은 메서드가 다른 실행 순서로 실행되면 다른 이벤트 결과가 발생할 때.
(2여러 부품이나 부품이 하나의 객체에装配될 수 있지만, 생성된 실행 결과가 다를 때.
(3제품 클래스가 매우 복잡하거나, 제품 클래스 내의 호출 순서가 다르면 다른 효과가 발생할 때, 이 때 Builder 모드를 사용하는 것이 매우 적합합니다.
(4객체를 초기화하는 과정이 매우 복잡할 때, 예를 들어 많은 매개변수가 있고 많은 매개변수가 기본값을 가지고 있을 때.
Builder 모드의 UML 클래스 다이어그램
역할 소개:
Product 제품 클래스——제품의 추상 클래스;
Builder——추상적 Builder 클래스, 제품 구성을 규범화하며, 일반적으로 서브 클래스에서 구체적인 구성 과정을 구현합니다;
ConcreateBuilder——구체적인 Builder 클래스;
Director——통일 조립 과정;
5. Builder 패턴의 간단한 구현
컴퓨터의 조립 과정은 상대적으로 복잡하며 조립 순서는 불정적입니다. 이해하기 쉽게 하기 위해, 컴퓨터의 조립 과정을 메인 보드 구축, 운영 체제 설정, 디스플레이 설정으로 간단화했습니다.3부분을 Director와 구체적인 Builder를 통해 컴퓨터 객체를 구축합니다.
예제 코드:
/** * 컴퓨터 추상 클래스, 즉 Product 역할 */ public abstract class Computer { protected String mBoard; protected String mDisplay; protected String mOS; protected Computer(){} /** * 보드 설정 * @param board */ public void setBoard(String board){ this.mBoard = board; } /** * 디스플레이 설정 * @param display */ public void setDisplay(String display){ this.mDisplay = display; } /** * 운영 체제 설정 */ public abstract void setOS(); @Override public String toString(){ return "Computer [mBoard=" + mBoard + ", mDisplay=" + mDisplay + ", mOS=" + mOS + "]"; } }
/** * Macbook이 Computer 클래스의 구체적인 예 */ public class Macbook extends Computer { protected Macbook(){} @Override public void setOS() { mOS = "Mac OS X 10"; } }
/** * 추상적 Builder 클래스 */ public abstract class Builder { /** * 보드 설정 * @param board */ public abstract void buildBoard(String board); /** * 디스플레이 설정 * @param display */ public abstract void buildDisplay(String display); /** * 운영 체제 설정 */ public abstract void buildOS(); /** * Computer 생성 * @return */ public abstract Computer create(); }
/** * MacbookBuilder라는 구체적인 Builder 클래스 */ public class MacbookBuilder extends Builder { private Computer mComputer = new Macbook(); @Override public void buildBoard(String board) { mComputer.setBoard(board); } @Override public void buildDisplay(String display) { mComputer.setDisplay(display); } @Override public void buildOS() { mComputer.setOS(); } @Override public Computer create() { return mComputer; } }
/** * Director 클래스는 Computer를 구축하는 책임을 가집니다. */ public class Director { Builder mBuilder = null; public Director(Builder builder){ mBuilder = builder; } /** * 구축 객체 * @param board 메인보드 * @param display 디스플레이 */ public void construct(String board, String display){ mBuilder.buildBoard(board); mBuilder.buildDisplay(display); mBuilder.buildOS(); } }
/** * 테스트 코드 */ public class Test { public static void main(String[] args){ //구축기 Builder builder = new MacbookBuilder(); //Director Director pcDirector = new Director(builder); //포장 구축 과정 pcDirector.construct("英特尔主板","Retina显示器"); //컴퓨터를 구성하고, 관련 정보를 출력}} System.out.println("Computer Info : " + builder.create().toString()); } }
출력 결과:
Computer Info : Computer [mBoard=英特尔主板, mDisplay=Retina显示器, mOS=Mac OS X 10]
위 예제에서는 MacbookBuilder를 사용하여 Macbook 객체를 구성하며, Director는 복잡한 제품 객체 구성 과정을 포장하고, 구성 세부 사항을 외부에 숨깁니다. Builder와 Director는 복잡한 객체의 구성과 표현을 분리하여, 동일한 구성 과정이 다른 객체를 생성할 수 있게 합니다.
실제 개발 과정에서 Director 역할은 자주 생략됩니다. Builder를 직접 사용하여 객체를 구성하면, 이 Builder는 일반적으로 연결 호출을 사용하며, 각 setter 메서드는 자신을 반환하므로 return this를 통해 setter 메서드가 연결 호출할 수 있습니다. 코드는 다음과 같습니다:
new TestBuilder() .setA("A") .create();
이와 같은 형식으로 Director 역할을 제거하고, 전체 구조가 더 간단해지고, Product 객체의 구성 과정에 더 세밀한 통제를 할 수 있습니다.
6. Builder 패턴 변형 - 연결 호출
예제 코드:
public class User { private final String name; //필수 private final String cardID; //필수 private final int age; //가능 private final String address; //가능 private final String phone; //가능 private User(UserBuilder userBuilder){ this.name=userBuilder.name; this.cardID=userBuilder.cardID; this.age=userBuilder.age; this.address=userBuilder.address; this.phone=userBuilder.phone; } public String getName() { return name; } public String getCardID() { return cardID; } public int getAge() { return age; } public String getAddress() {}} return address; } public String getPhone() { return phone; } public static class UserBuilder{ private final String name; private final String cardID; private int age; private String address; private String phone; public UserBuilder(String name,String cardID){ this.name=name; this.cardID=cardID; } public UserBuilder age(int age){ this.age=age; return this; } public UserBuilder address(String address){ this.address=address; return this; } public UserBuilder phone(String phone){ this.phone=phone; return this; } public User build(){ return new User(this); } } }
주의해야 할 점:
User 클래스의 생성자는 private입니다. 호출자는 User 객체를 직접 생성할 수 없습니다.
User 클래스의 속성은 모두 불변입니다. 모든 속성은 final修饰자를 추가하고, 생성자에서 값을 설정했습니다. 또한, 외부에서는 getters 메서드만 제공합니다.
Builder의 내부 클래스 생성자는 필수 파라미터만 받아서 final修饰자를 사용합니다.
호출 방식:
new User.UserBuilder("Jack","10086) .age(25) .address("GuangZhou") .phone("13800138000") .build();
앞에서 설명한 생성자와 setter와 비교하여/getter 메서드는 두 가지 방식으로, 더 좋은 가독성을 제공합니다. 유일한 문제는 추가적인 Builder 객체가 생성되어 메모리를 소비할 수 있다는 점입니다. 그러나 대부분의 경우 Builder 내부 클래스는 정적修饰된(static)를 사용하기 때문에, 이 문제는 크지 않습니다.
스레드 안전성에 관하여
Builder 모드는 비 스레드 안전합니다. Builder 내부 클래스에서 파라미터의 유효성을 확인하려면, 객체가 생성된 후에 확인해야 합니다.
정확한 예제:
public User build() { User user = new user(this); if (user.getAge() > 120) { throw new IllegalStateException("연령 범위 벗어남"); // 스레드 안전 } return user; }
에러 예제:
public User build() { if (age > 120) { throw new IllegalStateException("연령 범위 벗어남"); // 비스레드 안전 } return new User(this); }
7. Builder 패턴의 예제
1、Android에서 AlertDialog.Builder
private void showDialog(){ AlertDialog.Builder builder=new AlertDialog.Builder(context); builder.setIcon(R.drawable.icon); builder.setTitle("제목"); builder.setMessage("메시지"); builder.setPositiveButton("버튼",1", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //TODO } }); builder.setNegativeButton("버튼",2", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //TODO } }); builder.create().show(); }
2、OkHttp에서 OkHttpClient의 생성
OkHttpClient okHttpClient = new OkHttpClient.Builder() .cache(getCache()) .addInterceptor(new HttpCacheInterceptor()) .addInterceptor(new LogInterceptor()) .addNetworkInterceptor(new HttpRequestInterceptor()) .build();
3、Retrofit에서 Retrofit 객체의 생성
Retrofit retrofit = new Retrofit.Builder() .client(createOkHttp()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .baseUrl(BASE_URL) .build();
실제 사용에서 Director 역할을 생략한 경우가 많으며, 많은 프레임워크 소스 코드에서 Builder 패턴에 대해 다루는 경우, 대부분 클래식 GOF의 Builder 패턴이 아니라 더 간단한 구조를 선택합니다.
8. 장단점
점:
좋은 포장성으로 인해 클라이언트는 제품 내부 구현의 세부 사항을 알 필요가 없습니다
Builder는 독립적이고 확장성이 강합니다
단점:
잘못된 Builder 객체, Director 객체가 생성되어 메모리를 소비합니다
Android와 관련된 내용에 대해 더 많이 알고 싶은 독자는 다음 주제를 확인할 수 있습니다: 《Android 개발 입문 및 고급 강의》、《Android 디버깅 기술 및 일반 문제 해결 방법 요약》、《Android 기본 구성 요소 사용 요약》、《Android 뷰 View 기술 요약》、《Android 레이아웃 layout 기술 요약》 및 《Android 컨트롤러 사용 요약》
이 문서에서 설명된 내용이 모두 여러분의 Android 프로그램 설계에 도움이 되길 바랍니다.
선언: 이 문서의 내용은 인터넷에서 가져왔으며, 원저자의 소유물입니다. 내용은 인터넷 사용자가 자발적으로 기여하고 업로드한 것이며, 이 사이트는 소유권을 가지지 않으며, 인공 편집 처리를 하지 않았으며, 관련 법적 책임도 부담하지 않습니다. 저작권 문제가 의심되는 내용이 있다면, 이메일로 notice#w에 발송하여 주십시오.3codebox.com에 대한 신고는 이메일로 보내시고, #을 @으로 변경하여 신고하고 관련 증거를 제공하십시오. 실제로 확인되면, 이 사이트는 즉시 위반 내용을 삭제합니다.