Android 와 결합 하여 Builder 모드

33338 단어
머리말
Builder 모드, 대상 생 성 형 디자인 모드.디자인 모델 에 대해 말하자면 약간 심오 하 다 고 느 낄 수 있 습 니 다. 사실은 그렇지 않 습 니 다. 우 리 는 매일 코드 를 쓰 는 것 이 얼마나 적은 지 다양한 디자인 모델 과 접촉 하고 있 습 니 다. 다만 눈치 채 지 못 했 을 뿐 입 니 다.여기 서 Builder 모드 를 말씀 드 리 겠 습 니 다.
Android 의 Builder 모드
Android 개발 에 서 는 언제 Builder 모드 를 사용 합 니까?사실은 간단 하 다. 컨트롤 을 사용 하고 싶 을 때 나 대상 을 사용 하고 싶 을 때 그 를 직접 새로 만 들 수 없다 는 것 이다.그렇다면 이 컨트롤 (대상) 의 실현 은 대부분 Builder 모드 를 사용 한 것 이다.
AlertDialog.Builder
    private void InitView() {
        //      
        TextView mTextView = new TextView(this);
        Button mButton = new Button(this);

        //  Builder    Dialog
        AlertDialog.Builder builder=new 
                AlertDialog.Builder(this)
                .setTitle("My Dialog")
                .setMessage("This is Test Dialog")
                .setIcon(R.drawable.application_icon);
        AlertDialog dialog=builder.create();
    }

위의 코드 와 같이, 우 리 는 일반적인 방식 (new) 에 따라 TextView 대상 과 Button 대상 을 만 들 수 있 습 니 다. 그러나 AlertDialog 차례 가 되 었 을 때, 먼저 AlertDialog. Builder 대상 을 만 든 다음, 이 Builder 대상 을 통 해 AlertDialog 의 인 스 턴 스 를 만 들 수 있 습 니 다. 마찬가지 로 모두 Android 의 컨트롤 입 니 다. 차이 가 왜 이렇게 큽 니까? (Dialog 가 복잡 하기 때 문 입 니 다! o(╯□╰)o)。
다음은 AlertDialog 의 소스 코드 를 결합 하여 간단하게 분석 할 수 있다.
    protected AlertDialog(@NonNull Context context) {
        this(context, 0);
    }
  • 먼저 그의 구조 방법 을 보면 이 방법 은 proctected 로 수식 한 것 을 알 수 있다. 이 는 AlertDialog 의 하위 클래스 를 제외 하고 다른 종 류 는 이 방법 에 접근 할 수 없다 는 것 을 의미한다. AlertDialog 의 다른 두 가지 재 부팅 구조 방법 도 proctected 키 워드 를 사용 하여 수식 하고 관심 이 있 는 학생 들 은 스스로 소스 코드 를 참고 할 수 있 기 때문에 Activity 나 Fragment 에서저 희 는 AlertDialog 의 인 스 턴 스 를 직접 만 들 수 없 으 며 Builder 대상 을 통과 해 야 합 니 다.
  •         public static class Builder {
            private final AlertController.AlertParams P;
    
            public Builder(@NonNull Context context) {
                this(context, resolveDialogTheme(context, 0));
            }
            public Builder(@NonNull Context context, @StyleRes int themeResId) {
                P = new AlertController.AlertParams(new ContextThemeWrapper(
                        context, resolveDialogTheme(context, themeResId)));
                mTheme = themeResId;
            }
            public Builder setTitle(@Nullable CharSequence title) {
                P.mTitle = title;
                return this;
            }
            public Builder setMessage(@Nullable CharSequence message) {
                P.mMessage = message;
                return this;
            }
            public Builder setIcon(@DrawableRes int iconId) {
                P.mIconId = iconId;
                return this;
            }
    
        .....
    }
  • Builder 클래스 는 AlertDialog 내부 의 정적 클래스 입 니 다. 이 클래스 에는 매우 중요 한 속성 P 가 있 습 니 다. 이 변 수 는 final 형식 입 니 다. 그 밖 에 일련의 setxxx 방법 이 있 습 니 다. AlertDialog 의 서로 다른 속성 을 설정 하 는 데 사 용 됩 니 다. 예 를 들 어 위 에 열거 한 세 가지 방법 은 AlertDialog 의 Title, Message 및 Icon 정 보 를 각각 설정 할 수 있 습 니 다.이 방법 들 은 모두 파 라 메 터 를 앞에서 말 한 P 라 는 인 스 턴 스 에 직접 전달 한 것 이 고 모든 방법의 반환 값 은 Builder 류 자체 이기 때문에 개발 자 들 이 모든 방법 을 체인 으로 호출 하 는 데 편리 하 다. 이렇게 하면 쓰기 가 간단 할 뿐만 아니 라 읽 기 에 도 논리 적 이다.
  •         public AlertDialog create() {
                // We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
                // so we always have to re-set the theme
                final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
                P.apply(dialog.mAlert);
                dialog.setCancelable(P.mCancelable);
                if (P.mCancelable) {
                    dialog.setCanceledOnTouchOutside(true);
                }
                dialog.setOnCancelListener(P.mOnCancelListener);
                dialog.setOnDismissListener(P.mOnDismissListener);
                if (P.mOnKeyListener != null) {
                    dialog.setOnKeyListener(P.mOnKeyListener);
                }
                return dialog;
            }
  • 마지막 으로 Builder 류 의 create 방법 에서 AlertDialog 의 생 성 을 완 성 했 습 니 다. 종 류 를 떠 나 지 않 고 AlertDialog 의 사례 화 는 new 를 통 해 생 성 되 었 습 니 다. 그리고 앞에서 말 한 인 스 턴 스 P 와 dialog 인 스 턴 스 를 통 해 특정한 관 계 를 실현 하 였 습 니 다 (구체 적 으로 어떻게 실현 하 는 지 는 토론 하지 않 습 니 다)한 마디 로 하면 이전에 Builder 방법 으로 설정 한 일련의 매개 변 수 를 최종 AlertDialog 에 설정 한 것 입 니 다.
  • OKHttp 의 Request. Builder
    OKHttp 에 대해 서 는 모두 가 낯 설 지 않다 고 믿 습 니 다. Request 대상 을 구성 할 때 Request. Builder 를 사 용 했 습 니 다. 말 그대로 Builder 모드 도 사 용 했 습 니 다.
            findViewById(R.id.get).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    tv.setText("");
                    loading.setVisibility(View.VISIBLE);
                    OkHttpClient client = new OkHttpClient();
                    Request.Builder builder = new Request.Builder()
                            .url(BASE_URL)
                            .method("GET", null);
    
                    Request request = builder.build();
                    Call mCall = client.newCall(request);
                    mCall.enqueue(new MyCallback());
                }
            });

    이상 은 OKHttp 에 대한 전형 적 인 사용 방식 입 니 다. 여기 서 Request 대상 은 직접 만 드 는 것 이 아니 라 Request. Builder 대상 을 먼저 만 든 다음 그의 build 방법 으로 최종 request 대상 을 만 듭 니 다. Request 류 의 소스 코드 를 대충 볼 수 있 습 니 다.
    public final class Request {
      private final HttpUrl url;
      private final String method;
      private final Headers headers;
      private final RequestBody body;
      private final Object tag;
    
    
      private Request(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.headers = builder.headers.build();
        this.body = builder.body;
        this.tag = builder.tag != null ? builder.tag : this;
      }
        //     ....
    
      public static class Builder {
        private HttpUrl url;
        private String method;
        private Headers.Builder headers;
        private RequestBody body;
        private Object tag;
    
        public Builder() {
          this.method = "GET";
          this.headers = new Headers.Builder();
        }
    
    
        public Builder url(HttpUrl url) {
          if (url == null) throw new NullPointerException("url == null");
          this.url = url;
          return this;
        }
    
        /**
         * Sets the URL target of this request.
         *
         * @throws IllegalArgumentException if {@code url} is not a valid HTTP or HTTPS URL. Avoid this
         * exception by calling {@link HttpUrl#parse}; it returns null for invalid URLs.
         */
        public Builder url(String url) {
          if (url == null) throw new NullPointerException("url == null");
    
          // Silently replace websocket URLs with HTTP URLs.
          if (url.regionMatches(true, 0, "ws:", 0, 3)) {
            url = "http:" + url.substring(3);
          } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
            url = "https:" + url.substring(4);
          }
    
          HttpUrl parsed = HttpUrl.parse(url);
          if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
          return url(parsed);
        }
    
        /**
         * Sets the URL target of this request.
         *
         * @throws IllegalArgumentException if the scheme of {@code url} is not {@code http} or {@code
         * https}.
         */
        public Builder url(URL url) {
          if (url == null) throw new NullPointerException("url == null");
          HttpUrl parsed = HttpUrl.get(url);
          if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
          return url(parsed);
        }
    
        /**
         * Sets the header named {@code name} to {@code value}. If this request already has any headers
         * with that name, they are all replaced.
         */
        public Builder header(String name, String value) {
          headers.set(name, value);
          return this;
        }
    
    
        /** Removes all headers on this builder and adds {@code headers}. */
        public Builder headers(Headers headers) {
          this.headers = headers.newBuilder();
          return this;
        }
    
        public Builder method(String method, RequestBody body) {
          if (method == null) throw new NullPointerException("method == null");
          if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
          if (body != null && !HttpMethod.permitsRequestBody(method)) {
            throw new IllegalArgumentException("method " + method + " must not have a request body.");
          }
          if (body == null && HttpMethod.requiresRequestBody(method)) {
            throw new IllegalArgumentException("method " + method + " must have a request body.");
          }
          this.method = method;
          this.body = body;
          return this;
        }
    
    
        public Request build() {
          if (url == null) throw new IllegalStateException("url == null");
          return new Request(this);
        }
      }
    }
  • 우선 Request 는 final 형식 이기 때문에 하위 클래스 가 없 으 며 유일한 구조 방법 은 private 입 니 다. 따라서 개발 자 에 게 는 일반적인 방법 (new) 을 통 해 Request 대상 을 만 들 수 없습니다.
  • Builder 클래스 는 정적 내부 클래스 입 니 다. 구조 방법 을 통 해 알 수 있 듯 이 Request 의 기본 요청 방식 은 GET 요청 방식 입 니 다. 또한 Builder 대상 을 만 들 때 url 인 자 를 제공 하지 않 으 면 이상 을 던 집 니 다. 이것 은 합 리 적 이 고 필수 적 입 니 다. Http 요청 이 url 이 없 으 면 모든 것 이 공론 입 니 다. 여기 서 이상 을 던 지 는 것 이 필요 합 니 다. method 방법 에 서 는 매개 변수 에 따라 구체 적 인 요청 방법 을 수정 하 는 동시에 요청 방법 에 따라 RequestBody 가 필요 한 지 여 부 를 판단 합 니 다.
  • 마지막 으로 build 방법 으로 Request 를 만 들 었 습 니 다. 여기 서 호출 된 것 은 Request 의 유일한 구조 방법 입 니 다. 전달 하 는 매개 변 수 는 현재 Builder 인 스 턴 스 입 니 다. 이렇게 만 든 Request 대상 은 우리 가 만 든 Builder 인 스 턴 스 에 따라 맞 춤 형 Request 입 니 다. 다음 단계 에 동기 화 되 거나 비동기 적 인 네트워크 요청 을 할 수 있 습 니 다.
  • 이로써 이른바 Builder 모드 가 어떤 의미 가 있 는 지 의문 이 들 수 있 습 니 다.
    왜 Android 시스템 에서 AlertDialog 를 만 드 는 것 은 이렇게 복잡 합 니까? TextView 처럼 하나의 인 스 턴 스 를 직접 new 로 만 들 고 set 의 각종 속성 도 똑 같이 사용 할 수 있 지 않 습 니까? Request 대상 의 생 성 은 Builder 모드 를 사용 하지 않 아 도 됩 니까? 위의 여러 가지 이상 처리, 방법 집행 은 일반적인 방식 으로 도 가능 합 니 다. Builder 모드 의 가 치 는 어디 에 있 습 니까?
    이런 의문 을 가지 고 Builder 모드 를 잘 이해 해 봅 시다.
    빌 더 모드
    정의.
    복잡 한 대상 의 구축 과 그 표 시 를 분리 시 켜 같은 구축 과정 에서 서로 다른 표 시 를 만 들 수 있 습 니 다.
    적용 필드
    1. 같은 방법, 서로 다른 실행 순서, 서로 다른 사건 결과 발생 2. 여러 개의 부품 이나 부품 은 모두 같은 대상 에 조립 할 수 있 지만 발생 하 는 운행 결 과 는 다르다 3. 제품 류 가 매우 복잡 하거나 제품 의 호출 순서 가 다 르 면 서로 다른 역할 을 한다 4. 한 대상 이 매우 복잡 한 대상 을 초기 화해 야 한다. 이 대상 은 많은 매개 변수 가 있 고기본 값 이 있 음
    이러한 개념 을 보면 추상 적일 수 있 습 니 다. 다음은 코드 를 통 해 보 겠 습 니 다. 이전 공장 방법 모델 에서 우 리 는 공장 방법 모델 로 Mobike 가 Ofo 대상 에서 생 성 된 예 를 들 었 습 니 다. 여 기 는 여전히 두 가 지 를 예 로 들 어 Builder 모델 로 어떻게 쓰 는 지 보 겠 습 니 다.
    public final class Bicycle {
        public static final int SHARED = 1;
        public static final int PRIVATE = 0;
    
        @IntDef({SHARED, PRIVATE})
        @Retention(RetentionPolicy.SOURCE)
        public @interface bicycleType {
        }
    
        protected String color;
        protected String name;
        protected double charge;
        protected int number;
        protected int type;
    
        protected Bicycle(BicycleBuilder builder) {
            this.color = builder.color;
            this.name = builder.name;
            this.charge = builder.chager;
            this.number = builder.number;
            this.type = builder.type;
        }
    
        public static class BicycleBuilder {
    
    
            private String color;
            private String name;
            private double chager;
            private int number;
            private int type;
    
            public BicycleBuilder() {
                this.color = "  ";
                this.name = "  ";
                this.chager = 0;
                this.number = 0;
                this.type = Bicycle.PRIVATE;
            }
    
            public BicycleBuilder setColor(String color) {
                this.color = color;
                return this;
            }
    
            public BicycleBuilder setName(String name) {
                this.name = name;
                return this;
            }
    
            public BicycleBuilder setCharge(double chager) {
                this.chager = chager;
                return this;
            }
    
            public BicycleBuilder setNumber(int number) {
                this.number = number;
                return this;
            }
    
            public BicycleBuilder setType(@bicycleType int type) {
                this.type = type;
                return this;
            }
    
            public Bicycle build(){
                return new Bicycle(this);
            }
        }
    
        @Override
        public String toString() {
            String typeStr= type == SHARED ? "    ": "    ";
    
            return "Bicycle{" +
                    "color='" + color + '\'' +
                    ", name='" + name + '\'' +
                    ", charge=   " + charge +"/ "+
                    ", number=" + number +
                    ", type=" + typeStr +
                    '}';
        }
    }

    여기 서 Bicycle 클래스 는 5 개의 고유 한 속성 을 포함 하고 있 으 며, 동시에 그 구조 방법 을 proctected 로 설정 합 니 다. Bicycle Builder 클래스 를 통 해 Bicycle 을 만 드 는 인 스 턴 스 를 실현 합 니 다. 여기 서 Bicycle Builder 의 기본 구조 방법 은 일반적인 검은색 영구 표 개인 자전 거 를 만 들 것 입 니 다. Bicycle Builder 가 제공 하 는 몇 가지 방법 을 통 해 우 리 는 서로 다른 Bicycle 인 스 턴 스 를 만 들 수 있 습 니 다.예 를 들 어 다음 과 같은 실현:
    public class BuilderPatternActivity extends AppCompatActivity {
        private TextView bike_result;
        private Bicycle mBicycle;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_builder_pattern);
            bike_result = V.f(this, R.id.bike_result);
        }
    
        /**
         *      
         * @param view
         */
        public void NormalBike(View view) {
            Bicycle.BicycleBuilder builder=new Bicycle.BicycleBuilder();
            mBicycle=builder.build();
            updateView(mBicycle);
        }
        /**
         *     
         * @param view
         */
        public void Mobike(View view) {
            Bicycle.BicycleBuilder builder=new Bicycle.BicycleBuilder()
                    .setColor("  ")
                    .setName("    ")
                    .setCharge(1.0)
                    .setNumber(10010)
                    .setType(Bicycle.SHARED);
            mBicycle=builder.build();
            updateView(mBicycle);
        }
        /**
         * OFO   
         * @param view
         */
        public void Ofo(View view) {
            Bicycle.BicycleBuilder builder=new Bicycle.BicycleBuilder()
                    .setColor("  ")
                    .setName("OFO  ")
                    .setCharge(0.5)
                    .setNumber(40010)
                    .setType(Bicycle.SHARED);
            mBicycle=builder.build();
            updateView(mBicycle);
        }
    
    
        private void updateView(Bicycle mBicycle) {
            bike_result.setText("");
            bike_result.setText(mBicycle.toString());
        }
    }

    Bicycle. Bicycle Builder 가 제공 하 는 일련의 set 방법 을 통 해 우 리 는 mobike 인 스 턴 스 와 of 자전거 인 스 턴 스 를 만 들 었 습 니 다.
    이것 이 바로 Builder 모드 입 니 다. 장면 에서 언급 한 바 와 같이 Builder 모드 를 통 해 같은 구축 과정 에서 우 리 는 서로 다른 결 과 를 만 들 수 있 습 니 다. 일반적으로 우리 가 만 들 대상 은 매우 복잡 하고 많은 매개 변수 가 있 습 니 다. 이런 매개 변수 중 일 부 는 필수 적 입 니 다. 예 를 들 어 OKHttp 에서 Request 의 url 매개 변 수 는 기본 값 도 있 습 니 다. 한 마디 로 하면, Builder 모드, 대상 생 성 형 디자인 모델, 우리 가 대상 을 만 드 는 데 방향 을 제공 합 니 다.
    마지막 으로 Builder 모드 를 사용 한 것 - RxJava. RxJava 하면 관찰자 모드 를 쉽게 떠 올 릴 수 있 습 니 다. 좋 습 니 다. RxJava 의 가장 핵심 적 인 사상 은 관찰자 모드 입 니 다. 하지만 RxJava 를 사용 하 는 과정 을 생각해 보 세 요.
            ArrayList datas = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                datas.add("item_" + i);
            }
            Observable.just(datas)
                    .flatMap(new Func1, Observable>() {
                        @Override
                        public Observable call(ArrayList strings) {
                            return Observable.from(strings);
                        }
                    })
                    .map(new Func1() {
                        @Override
                        public Integer call(String s) {
                            return s.hashCode();
                        }
                    })
                    .subscribe(new Action1() {
                        @Override
                        public void call(Integer Integer) {
                            Log.e(MainActivity.class.getSimpleName(), "call---->" + Integer);
                        }
                    });

    위 코드 에서 보 듯 이 subscribe 방법 이 실행 되 기 전에 다양한 조작 자 를 통 해 원시 데이터 인 Array List 는 Integer 형식의 데이터 가 되 었 습 니 다. 즉, 우리 가 조작 자 를 사용 하 는 과정 은 Builder 모델 을 구축 하 는 과정 입 니 다. 우리 가 최종 적 으로 필요 로 하 는 제품 이 생 성 될 때 까지 입 니 다. 이것 은 Builder 모델 의 정의 와 사용 장면 과 완전히 일치 합 니 다.맞았어
    Builder 모드 VS 공장 방법 모드
    공장 모델 은 일반적으로 하나의 제품 을 만 드 는 것 입 니 다. 이 제품 을 만 드 는 것 을 중시 합 니 다. 만 들 기만 하면 이 제품 의 구성 부분 에 관심 이 없습니다. 코드 로 볼 때 공장 모델 은 하나의 방법 입 니 다. 이 방법 으로 제품 을 생산 할 수 있 습 니 다.
    건설 자 모델 도 하나의 제품 을 만 들 지만 이 제품 을 만 들 뿐만 아니 라 이 제품 의 구성 디 테 일, 구성 과정 과 도 관련 이 있다. 코드 를 보면 건설 자 모델 은 제품 을 만 들 때 이 제품 은 여러 가지 방법 이 있다. 건설 자 모델 은 이 같은 방법 이지 만 실행 순서에 따라 서로 다른 디 테 일 을 구성 하 는 제품 을 만든다.
    공장 모델 은 전체 에 관심 을 가지 고 건설 자 모델 은 세부 사항 에 관심 을 가진다.
    마지막.
    지금 우리 가 전에 제기 한 문제 로 돌아 가면 Builder 모델 의 의 미 는 무엇 입 니까? 다 본 후에 당신 은 이미 답 을 얻 었 을 것 입 니 다. 실질 적 인 의미 가 없습니다. Builder 모델 의 사용 은 우리 의 코드 운행 속 도 를 가속 화하 지 않 습 니 다. 디자인 모델 은 전체적으로 포장, 계승, 다 형 과 관련 된 반복 적 인 사용 입 니 다. 프로 그래 밍 기술 로 우리 가 쓸 수 있 습 니 다.고 품질 코드 의 기교.
    마지막 으로 다시 한 번 말씀 드 리 지만, 엄 밀 히 말 하면 본 논문 에서 논의 한 Builder 모델 은 표준 적 인 의미 의 Builder 모델 이 아 닙 니 다. 여기 서 우 리 는 Android 소스 코드 의 측면 에서 Builder 모델 을 간소화 하고 체인 호출 과 습관 을 편리 하 게 하기 위해 원래 있 던 Director 역할 을 버 렸 습 니 다. 정통 적 인 Builder 모델 에 관심 이 있 는 학생 들 은 다시 깊이 연구 할 수 있 습 니 다.

    좋은 웹페이지 즐겨찾기