1.设计模式之终极结合

昨天项目上线(今年是没有新需求了,那提前祝大家新年快乐),上班继续最近几期博客的编写。承接上一篇博客观察者模式,这一期我们一起来了解一下Builder模式。我们最近几期博客打算主要讲一下单例模式、观察者模式、Build模式,目的是为了方便后期为大家带来RXJava+Retrofit2.0+Okhttp3结合使用的网络请求框架。

思考了一上午,我该怎么写出Builder模式的精华呢?(如果你们有一定要告诉我)

2.Builder模式定义

简单说就是:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

3.Builder模式理解

看了定义觉得难以理解,那么说说我的理解。比如说你需要构造一个机器:是不是会包含很多部件,但是根据相同的制造过程可以制作出不同的机器(机器猫,机器狗,机器人,机器*等等)。再比如说相同的注册流程可以注册出不同的公司(百度,华为,小米等等),不同的注册信息可以表示出不同的公司。

然后我们再说说为什么要用Builder模式吧,比如你做个机器人,我们是不是要用很多参数(手,脚,关节,眼睛,嘴巴,鼻子等等)来构造,如果你用一般的构造方法,你觉得你要写多少个构造方法呢?显得特别麻烦,代码还不清晰,传参也麻烦;这样我们就引入了Builder模式。

4.Builder模式简单示例

步骤一:创建一个实体类CompanyClient,里面有个和实体类(只能获取变量值)相同变量的静态内部类Builder(设置变量值) 。在实体类通过构造函数来构造一个Builder,通过调用build()来建造一个具体的实体对象。


/*** 实体类 包含一个静态内部类 Builder*/
public class CompanyClient {public final String companyName;public final String companyAddress;public final double companyRegfunds;public final String mPerson;public final String mType;//构造方法 public CompanyClient() {this(new Builder());}//构造方法public CompanyClient(Builder builder){this.companyName = builder.companyName;this.companyAddress = builder.companyAddress;this.companyRegfunds = builder.companyRegfunds;this.mPerson = builder.person;this.mType = builder.type;}public String getCompanyName() {return companyName;}public String getCompanyAddress() {return companyAddress;}public double getCompanyRegfunds() {return companyRegfunds;}public String getmPerson() {return mPerson;}public String getmType() {return mType;}public Builder newBuilder() {return new Builder(this);}@Overridepublic String toString() {return "CompanyClient{" +"companyName='" + companyName + '\'' +", companyAddress='" + companyAddress + '\'' +", companyRegfunds=" + companyRegfunds +"千万"+", mPerson=" + mPerson +", mType='" + mType + '\'' +'}';}/***静态内部类 Builder*/public static class Builder{public String companyName;public String companyAddress;public  double companyRegfunds;public  String person;public String type;//构造方法public Builder() {companyName = companyName;companyAddress = companyAddress;companyRegfunds = companyRegfunds;person = person;type = type;}//构造方法Builder(CompanyClient companyClient){this.companyName = companyClient.companyName;this.companyAddress = companyClient.companyAddress;this.companyRegfunds = companyClient.companyRegfunds;this.person = companyClient.mPerson;this.type = companyClient.mType;}public Builder setCompanyName(String name) {companyName = name;return this;}public Builder setCompanyAddress(String address) {companyAddress = address;return this;}public Builder setCompanyRegfunds(double regfunds) {companyRegfunds = regfunds;return this;}public Builder setmPerson(String per) {person = per;return this;}public Builder setmType(String typeStr) {type = typeStr;return this;}//构建一个实体public CompanyClient build() {return new CompanyClient(this);}}
}

 步骤二:实现类(TestBuilder)

public class TestBuilder {public static void main(String[] args) {CompanyClient client = new CompanyClient.Builder().setCompanyName("百度").setCompanyAddress("海定区百度大厦").setCompanyRegfunds(5).setmPerson("1000人以上").build();System.out.println("构造出一个公司:" + client.toString());System.out.println("---------------------分隔符");CompanyClient.Builder builder = new CompanyClient.Builder();builder.setCompanyName("华为");builder.setCompanyAddress("海定区百度大厦");builder.setCompanyRegfunds(20);builder.setmType("通信科技行业");CompanyClient client1 = builder.build();System.out.println("构造出另一个公司:" + client1.toString());}
}

dos命令运行输出结果: 

5.Builder模式库拓展

用到Builder模式的有ImageLoader,Glide,Okhttp,Retrofit以及Android AlertDialog 等等;今天我们就以现在运用最广泛的Okhttp做简单分析:

  • 先来看一下Okhttp的注释
/*** Factory for {@linkplain Call calls}, which can be used to send HTTP requests and read their* responses.** <h3>OkHttpClients should be shared</h3>** <p>OkHttp performs best when you create a single {@code OkHttpClient} instance and reuse it for* all of your HTTP calls. This is because each client holds its own connection pool and thread* pools. Reusing connections and threads reduces latency and saves memory. Conversely, creating a* client for each request wastes resources on idle pools.** <p>Use {@code new OkHttpClient()} to create a shared instance with the default settings:* <pre>   {@code**   // The singleton HTTP client.*   public final OkHttpClient client = new OkHttpClient();* }</pre>** <p>Or use {@code new OkHttpClient.Builder()} to create a shared instance with custom settings:* <pre>   {@code**   // The singleton HTTP client.*   public final OkHttpClient client = new OkHttpClient.Builder()*       .addInterceptor(new HttpLoggingInterceptor())*       .cache(new Cache(cacheDir, cacheSize))*       .build();* }</pre>** <h3>Customize your client with newBuilder()</h3>** <p>You can customize a shared OkHttpClient instance with {@link #newBuilder()}. This builds a* client that shares the same connection pool, thread pools, and configuration. Use the builder* methods to configure the derived client for a specific purpose.** <p>This example shows a call with a short 500 millisecond timeout: <pre>   {@code**   OkHttpClient eagerClient = client.newBuilder()*       .readTimeout(500, TimeUnit.MILLISECONDS)*       .build();*   Response response = eagerClient.newCall(request).execute();* }</pre>*/
/**以上是okhttp的部分注释,大概说的就是:这个工程被用于网络访问,被返回相应的结果;
*我们在使用它的时候需要使用单例模式来保证整个网络请求中只存在一个实例,从而减少访问时间以及节约内存。
*您可以使用{@link#newBuilder()}自定义共享的okhtp客户端实例。这构建了一个共享相同连接池、线程池
*和配置的客户端。使用Builder方法来达到配置定制的客户端。
*/
  •  再来看一下okhttp的部分源码(删减版)
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {final Dispatcher dispatcher;final Proxy proxy;final List<Protocol> protocols;final List<ConnectionSpec> connectionSpecs;final List<Interceptor> interceptors;final List<Interceptor> networkInterceptors;final ProxySelector proxySelector;final CookieJar cookieJar;final Cache cache;final InternalCache internalCache;final SocketFactory socketFactory;final SSLSocketFactory sslSocketFactory;final CertificateChainCleaner certificateChainCleaner;final HostnameVerifier hostnameVerifier;final CertificatePinner certificatePinner;final Authenticator proxyAuthenticator;final Authenticator authenticator;final ConnectionPool connectionPool;final Dns dns;final boolean followSslRedirects;final boolean followRedirects;final boolean retryOnConnectionFailure;final int connectTimeout;final int readTimeout;final int writeTimeout;final int pingInterval;//构造函数 通过静态内部类构造函数Builder()传入值public OkHttpClient() {this(new Builder());}//构造函数OkHttpClient(Builder builder) {this.dispatcher = builder.dispatcher;this.proxy = builder.proxy;this.protocols = builder.protocols;this.connectionSpecs = builder.connectionSpecs;this.interceptors = Util.immutableList(builder.interceptors);this.networkInterceptors = Util.immutableList(builder.networkInterceptors);this.proxySelector = builder.proxySelector;this.cookieJar = builder.cookieJar;this.cache = builder.cache;this.internalCache = builder.internalCache;this.socketFactory = builder.socketFactory;boolean isTLS = false;for (ConnectionSpec spec : connectionSpecs) {isTLS = isTLS || spec.isTls();}if (builder.sslSocketFactory != null || !isTLS) {this.sslSocketFactory = builder.sslSocketFactory;this.certificateChainCleaner = builder.certificateChainCleaner;} else {X509TrustManager trustManager = systemDefaultTrustManager();this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);}this.hostnameVerifier = builder.hostnameVerifier;this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(certificateChainCleaner);this.proxyAuthenticator = builder.proxyAuthenticator;this.authenticator = builder.authenticator;this.connectionPool = builder.connectionPool;this.dns = builder.dns;this.followSslRedirects = builder.followSslRedirects;this.followRedirects = builder.followRedirects;this.retryOnConnectionFailure = builder.retryOnConnectionFailure;this.connectTimeout = builder.connectTimeout;this.readTimeout = builder.readTimeout;this.writeTimeout = builder.writeTimeout;this.pingInterval = builder.pingInterval;}/** Default connect timeout (in milliseconds). */public int connectTimeoutMillis() {return connectTimeout;}/** Default read timeout (in milliseconds). */public int readTimeoutMillis() {return readTimeout;}/** Default write timeout (in milliseconds). */public int writeTimeoutMillis() {return writeTimeout;}/** Web socket ping interval (in milliseconds). */public int pingIntervalMillis() {return pingInterval;}public Proxy proxy() {return proxy;}public ProxySelector proxySelector() {return proxySelector;}public CookieJar cookieJar() {return cookieJar;}public Cache cache() {return cache;}InternalCache internalCache() {return cache != null ? cache.internalCache : internalCache;}public Dns dns() {return dns;}/*** Prepares the {@code request} to be executed at some point in the future.*/@Override public Call newCall(Request request) {return new RealCall(this, request, false /* for web socket */);}public Builder newBuilder() {return new Builder(this);}public static final class Builder {Dispatcher dispatcher;Proxy proxy;List<Protocol> protocols;List<ConnectionSpec> connectionSpecs;final List<Interceptor> interceptors = new ArrayList<>();final List<Interceptor> networkInterceptors = new ArrayList<>();ProxySelector proxySelector;CookieJar cookieJar;Cache cache;InternalCache internalCache;SocketFactory socketFactory;SSLSocketFactory sslSocketFactory;CertificateChainCleaner certificateChainCleaner;HostnameVerifier hostnameVerifier;CertificatePinner certificatePinner;Authenticator proxyAuthenticator;Authenticator authenticator;ConnectionPool connectionPool;Dns dns;boolean followSslRedirects;boolean followRedirects;boolean retryOnConnectionFailure;int connectTimeout;int readTimeout;int writeTimeout;int pingInterval;//构造public Builder() {dispatcher = new Dispatcher();protocols = DEFAULT_PROTOCOLS;connectionSpecs = DEFAULT_CONNECTION_SPECS;proxySelector = ProxySelector.getDefault();cookieJar = CookieJar.NO_COOKIES;socketFactory = SocketFactory.getDefault();hostnameVerifier = OkHostnameVerifier.INSTANCE;certificatePinner = CertificatePinner.DEFAULT;proxyAuthenticator = Authenticator.NONE;authenticator = Authenticator.NONE;connectionPool = new ConnectionPool();dns = Dns.SYSTEM;followSslRedirects = true;followRedirects = true;retryOnConnectionFailure = true;connectTimeout = 10_000;readTimeout = 10_000;writeTimeout = 10_000;pingInterval = 0;}//构造Builder(OkHttpClient okHttpClient) {this.dispatcher = okHttpClient.dispatcher;this.proxy = okHttpClient.proxy;this.protocols = okHttpClient.protocols;this.connectionSpecs = okHttpClient.connectionSpecs;this.interceptors.addAll(okHttpClient.interceptors);this.networkInterceptors.addAll(okHttpClient.networkInterceptors);this.proxySelector = okHttpClient.proxySelector;this.cookieJar = okHttpClient.cookieJar;this.internalCache = okHttpClient.internalCache;this.cache = okHttpClient.cache;this.socketFactory = okHttpClient.socketFactory;this.sslSocketFactory = okHttpClient.sslSocketFactory;this.certificateChainCleaner = okHttpClient.certificateChainCleaner;this.hostnameVerifier = okHttpClient.hostnameVerifier;this.certificatePinner = okHttpClient.certificatePinner;this.proxyAuthenticator = okHttpClient.proxyAuthenticator;this.authenticator = okHttpClient.authenticator;this.connectionPool = okHttpClient.connectionPool;this.dns = okHttpClient.dns;this.followSslRedirects = okHttpClient.followSslRedirects;this.followRedirects = okHttpClient.followRedirects;this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;this.connectTimeout = okHttpClient.connectTimeout;this.readTimeout = okHttpClient.readTimeout;this.writeTimeout = okHttpClient.writeTimeout;this.pingInterval = okHttpClient.pingInterval;}/*** Sets the default connect timeout for new connections. A value of 0 means no timeout,* otherwise values must be between 1 and {@link Integer#MAX_VALUE} when converted to* milliseconds.*/public Builder connectTimeout(long timeout, TimeUnit unit) {connectTimeout = checkDuration("timeout", timeout, unit);return this;}/*** Sets the default read timeout for new connections. A value of 0 means no timeout, otherwise* values must be between 1 and {@link Integer#MAX_VALUE} when converted to milliseconds.*/public Builder readTimeout(long timeout, TimeUnit unit) {readTimeout = checkDuration("timeout", timeout, unit);return this;}/*** Sets the default write timeout for new connections. A value of 0 means no timeout, otherwise* values must be between 1 and {@link Integer#MAX_VALUE} when converted to milliseconds.*/public Builder writeTimeout(long timeout, TimeUnit unit) {writeTimeout = checkDuration("timeout", timeout, unit);return this;}/*** Sets the interval between web socket pings initiated by this client. Use this to* automatically send web socket ping frames until either the web socket fails or it is closed.* This keeps the connection alive and may detect connectivity failures early. No timeouts are* enforced on the acknowledging pongs.** <p>The default value of 0 disables client-initiated pings.*/public Builder pingInterval(long interval, TimeUnit unit) {pingInterval = checkDuration("interval", interval, unit);return this;}private static int checkDuration(String name, long duration, TimeUnit unit) {if (duration < 0) throw new IllegalArgumentException(name + " < 0");if (unit == null) throw new NullPointerException("unit == null");long millis = unit.toMillis(duration);if (millis > Integer.MAX_VALUE) throw new IllegalArgumentException(name + " too large.");if (millis == 0 && duration > 0) throw new IllegalArgumentException(name + " too small.");return (int) millis;}/*** Sets the HTTP proxy that will be used by connections created by this client. This takes* precedence over {@link #proxySelector}, which is only honored when this proxy is null (which* it is by default). To disable proxy use completely, call {@code setProxy(Proxy.NO_PROXY)}.*/public Builder proxy(Proxy proxy) {this.proxy = proxy;return this;}/*** Sets the proxy selection policy to be used if no {@link #proxy proxy} is specified* explicitly. The proxy selector may return multiple proxies; in that case they will be tried* in sequence until a successful connection is established.** <p>If unset, the {@link ProxySelector#getDefault() system-wide default} proxy selector will* be used.*/public Builder proxySelector(ProxySelector proxySelector) {this.proxySelector = proxySelector;return this;}/*** Sets the handler that can accept cookies from incoming HTTP responses and provides cookies to* outgoing HTTP requests.** <p>If unset, {@linkplain CookieJar#NO_COOKIES no cookies} will be accepted nor provided.*/public Builder cookieJar(CookieJar cookieJar) {if (cookieJar == null) throw new NullPointerException("cookieJar == null");this.cookieJar = cookieJar;return this;}/** Sets the response cache to be used to read and write cached responses. */void setInternalCache(InternalCache internalCache) {this.internalCache = internalCache;this.cache = null;}/** Sets the response cache to be used to read and write cached responses. */public Builder cache(Cache cache) {this.cache = cache;this.internalCache = null;return this;}/*** Sets the DNS service used to lookup IP addresses for hostnames.** <p>If unset, the {@link Dns#SYSTEM system-wide default} DNS will be used.*/public Builder dns(Dns dns) {if (dns == null) throw new NullPointerException("dns == null");this.dns = dns;return this;}public Builder addNetworkInterceptor(Interceptor interceptor) {networkInterceptors.add(interceptor);return this;}//通过build()来建造一个OkHttpClent实例public OkHttpClient build() {return new OkHttpClient(this);}}
}

我们来看看怎么使用:

  • 直接使用Okhttp
public final mOkHttpClient = new OkHttpClient();Request request = new Request.Builder().url("your url").addHeader("","").post(requestBody).build();Response response = mOkHttpClient.newCall(request).execute();
  •  Retrofit+okhttp结合使用
//Retrofit+okhttp,支持Rxjava,gson解析
public class RetrofitHelper {private static OkHttpClient mOkHttpClient;private RetrofitHelper() {initOkHttpClient();}/*** 静态内部类,实例化对象使用*/private static class SingleRetrofitHelper {private static final RetrofitHelper INSTANCE = new RetrofitHelper();}/*** 对外唯一实例的接口** @return*/public static RetrofitHelper getInstance() {return SingleRetrofitHelper.INSTANCE;}public ShopkeeperApi getApi() {Retrofit retrofit = new Retrofit.Builder().baseUrl(Config.BASE_URL).client(mOkHttpClient)//支持gson解析.addConverterFactory(GsonConverterFactory.create())//支持rxjava.addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();return retrofit.create(ShopkeeperApi.class);}/*** 初始化OKHttpClient 这就是DCL模式下通过Builder模式来实例化一个OkHttpClient*/private static void initOkHttpClient(){//日志过滤HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);if (mOkHttpClient == null) {synchronized (RetrofitHelper.class) {if (mOkHttpClient == null) {mOkHttpClient = new OkHttpClient.Builder().addInterceptor(interceptor).retryOnConnectionFailure(true).connectTimeout(5, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS).build();}}}}
}

6.Builder模式总结

  • 优点:我可以不必知道你的内部构造是怎样的,我可以直接使用Builder建造自己需要的客户端;代码清晰,易维护,易扩展;将构造和表示分离,降低耦合
  • 缺点:代码也可能不清晰,不易维护(怎么说:比如你的客户端实现了很多接口,当你每当修改接口的时候,每次都要对应修改你的客户端);使用不恰当消耗内存

大概就说这么多吧,期待接下来的Retrofit+okhttp+RXJava来实现网络请求。


其他设计模式:

  • 单例模式
  • 观察者模式

Java Builder模式(设计模式之Builder模式)相关推荐

  1. java 23 种设计模式-23-java 策略模式(Strategy Pattern)

    目录 目录 策略模式 实际案例 定义 测试 实现方式 UML & Code UML Code 系列导航 策略模式 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运 ...

  2. Java中的设计模式:“代理模式”的理解

    代理模式定义: 为其他对象提供一种代理以控制对这个对象的访问.在面向对象中,有时候直接访问一些对象比较麻烦,所以代理模式就是在这个对象上加上一个访问该对象的访问层.类似于很多明星的事务实际都是交给经纪 ...

  3. java 访客模式,设计模式 - 访客模式( Visitor Pattern)

    设计模式 - 访客模式( Visitor Pattern) 在Visitor模式中,我们使用一个访问者类来更改元素类的执行算法. 通过这种方式,元素的执行算法可以随着访问者的变化而变化. 此模式属于行 ...

  4. java view template_Java设计模式之模板方法模式(Template Method)

    本文继续介绍23种设计模式系列之模板方法模式. 概述 模板方法模式是类的行为模式.准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑.不同的子 ...

  5. Java总结之设计模式(工厂模式)

    1.Java设计模式分类 1.1 创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式. 1.2 结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组 ...

  6. 装饰模式java咖啡_Java设计模式——装饰者模式

    概述: 本章可以称为"给爱用继承的人一个全新的设计眼界".我们即将再度探讨典型滥用问题.你将在本章学到如何使用对象组合的方式,做到在运行时装饰类.为什么呢?一旦你熟悉了装饰者的技巧 ...

  7. Java二十三设计模式之-----原型模式

    一.原型模式(Prototype) 原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制.克隆,产生一个和原对象类似的新对象.本小结会通 ...

  8. java学习之设计模式-享元模式

    享元模式 概述 定义: 运用共享技术来有效地支持大量细粒度对象的复用.它通过共享已经存在的对象来大幅度减少需要创建的对象数量.避免大量相似对象的开销,从而提高系统资源的利用率. 结构 享元(Flywe ...

  9. 访问者模式的java语言_Java 设计模式 之 访问者模式

    package com.visit.theory; import java.util.ArrayList; public class Test { public static void main(St ...

  10. java gof_java GOF23设计模式-简单工厂模式进阶

    不修改已有代码,而是添加代码 不和所有类打交道,只和总接口的实现类打交道 public class Cilent { public static void main(String[] args) { ...

最新文章

  1. csu 1554: SG Value 思维题
  2. 【数字信号处理】相关函数 ( 功率信号 | 功率信号的互相关函数 | 功率信号的自相关函数 )
  3. 代码覆盖率工具 Istanbul 入门教程
  4. matlab 多维数组申明
  5. Linux LVM管理
  6. springboot的war部署到tomcat正常启动,访问404问题
  7. 如何使用TrueCrypt加密
  8. 【愚公系列】2022年01月 Django商城项目10-首页-页面设计
  9. 如何去掉PDF右下角的全能扫描王水印
  10. 1: 上山拜师--ARM简介
  11. FPGA_Verilog学习之旅(2)---浅谈VGA
  12. C语言函数题-输入多个单词,统计以指定字母开头的单词个数
  13. 美女照相怎么摆pose
  14. android 一键接入新浪微博,腾讯微博,人人网,QQ空间,微信好友圈 (只需5分钟)
  15. 城市大脑与超级智能城市建设规范研究
  16. networkx 画图布局
  17. 【思特奇杯·云上蓝桥-算法集训营】第1周——了解算法与数据结构
  18. 重读经典:《End-to-End Object Detection with Transformers》
  19. 物联网技术可以给养老院带来哪些变化
  20. 俄罗斯最大银行宣布加入区块链联盟…

热门文章

  1. Acer EC-471G BIOS升级注意事项
  2. linux安装软件统一格式化,Ubuntu 14.04安装格式转换软件Format Junkie 1.07
  3. Python编程之二维码生成
  4. java从JDK里提取全球数据,包含国家(名称、二字代码、三字代码)、币种(名称和代码)、和语言种类的数据
  5. 360浏览器下页面样式显示异常的解决方法
  6. Unity 获取GPS经纬度
  7. 绿联USB网卡的使用记录
  8. 苹果4如何添加时间插件_【苹果搞机】苹果设备越狱后的第一步如何添加软件源...
  9. MATLAB【工具箱下载】汇总
  10. xshell 4 中文乱码问题解决