(6).模式扩展
建造者模式除了上面的用途外,在开发中还有一个常用的使用方式,就是当一个类构造器需要传入很多参数时,如果创建这个类的实列,代码可读性会非常差,而且很容易引入错误
,此时就应该利用建造者模式进行重构。
package com.jsxs.pattern.build.demo2; /** * @Author Jsxs * @Date 2023/4/18 17:18 * @PackageName:com.jsxs.pattern.build.demo2 * @ClassName: Phone * @Description: TODO * @Version 1.0 */ public class Phone { private String cpu; private String Screen; private String money; private String mainBoard; // 私有构造方法 private Phone(Builder builder) { this.cpu = builder.cpu; this.Screen = builder.Screen; this.money = builder.money; this.mainBoard = builder.mainBoard; } // 静态内部类 public static final class Builder{ private String cpu; private String Screen; private String money; private String mainBoard; public Builder cpu(String cpu) { this.cpu = cpu; return this; } public Builder screen(String screen) { this.Screen = screen; return this; } public Builder money(String money) { this.money = money; return this; } public Builder mainBoard(String mainBoard) { this.mainBoard = mainBoard; return this; } public Phone build(){ return new Phone(this); // 传递这个内部类 } @Override public String toString() { return "Builder{" + "cpu='" + cpu + '\'' + ", Screen='" + Screen + '\'' + ", money='" + money + '\'' + ", mainBoard='" + mainBoard + '\'' + '}'; } } @Override public String toString() { return "Phone{" + "cpu='" + cpu + '\'' + ", Screen='" + Screen + '\'' + ", money='" + money + '\'' + ", mainBoard='" + mainBoard + '\'' + '}'; } }
package com.jsxs.pattern.build.demo2; /** * @Author Jsxs * @Date 2023/4/18 17:26 * @PackageName:com.jsxs.pattern.build.demo2 * @ClassName: Client * @Description: TODO * @Version 1.0 */ public class Client { public static void main(String[] args) { // 利用建造者创建对象 ---- 因为这些方法的返回值对象都是当前对象,所以可以使用链式编程 Phone.Builder builder = new Phone.Builder() .cpu("inter") .screen("三星") .mainBoard("苹果") .money("1200"); Phone phone = builder.build(); System.out.println(phone); } }
重构后的代码在使用起来更方便,某种程度上可以提高开发效率。从软件设计,对程序员的要求比较高。
7.创建者模式对比
(1).工厂方法模式VS建造者模式
工厂方法模式注重的是整体对象的创建方式(结果)
;而建造者模式注重的是部件构建的过程(过程)
,意在通过一步步地精确构造出一个复杂地对象。
我们举个简单的列子来说明两者地差异: 如要制造一个超人,如果使用工厂方法模式,直接生产出来地就是一个力大无穷、能够飞翔、内裤外穿地超人;而如果使用建造者模式,则需要组装收、头、脚、驱赶,然后再把内裤外穿,于是一个超人就诞生了。
(2).抽象工厂模式VS建造者模式
抽象工厂模式实现对产品家族地创建
,一个产品家族是这样地一系列产品;具有不同分类欸都地产品组合,采用抽象工厂模式是不需要关心构建过程,只关心什么产品由什么工厂生产
即可。
建造者模式则是要求按照指定地蓝图建造产品,他的主要目的是通过组件装零配件而生产地一个新产品
。
如果将抽象工厂模式看成汽车配件生产工厂
,生产一个产品族地产品.那么建造者模式就是一个汽车组装工厂
,通过对部件地组装可以返回一量完整的汽车。
(三)、结构性模式(7种)
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构性模式
和对象结构型模式
,前者采用继承机制来组织接口和类,后者采用组合或聚合来组合对象。
由于组合关系或聚合关系比继承关系的耦合度低,满足合成复用原则,所以对象结构型模式
比类结构型模式
具有更大的灵活性。
1.代理模式
(1).概述
由于某些原因需要给某个对象(结婚对象)提供一个代理(婚介)以控制对该对象的访问(结婚仪式)。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介
。
eg: 结婚: 找婚介。
Java中的代理按照代理类生成时机不同分为静态代理
和动态代理
。静态代理类在编译期就生成
,而动态代理类则是在Java运行时动态生成
。动态代理又有JDK和GCLib代理两种。
(2).结构
代理模式分为三种角色:
- 抽象主题类: 通过
接口或抽象类
声明真实主题和代理对象实现的业务方法(结婚)。 - 真实主题类:实现了抽象主题的
具体业务
,是代理对象所代表的真实对象,是最终要引入的对象(新人)。 - 代理类: 提供了与真实主题相同的接口,其内部含有对真实主题的引用,他可以访问、控制或扩展真实主题的功能(婚介所)。
(3).静态代理
我们通过案列来感觉一下静态代理。
eg: 火车站买票
如果要买火车票的话,我们需要去火车站买票,排队等一系列的操作,显然比较麻烦。而火车站多个地方都有代售点,我们去代售点买火车票就方便很多了。这个列子其实就是典型的代理模式,火车站是目标对象
,代售点是代理对象
。
抽象主题类
package com.jsxs.structure.proxy.static_proxy; /** * @Author Jsxs * @Date 2023/4/19 13:40 * @PackageName:com.jsxs.structure.proxy.static_proxy * @ClassName: SellTickets * @Description: TODO 卖火车票的接口 * @Version 1.0 */ public interface SellTickets { void sell(); }
真实主题类
package com.jsxs.structure.proxy.static_proxy; /** * @Author Jsxs * @Date 2023/4/19 13:41 * @PackageName:com.jsxs.structure.proxy.static_proxy * @ClassName: TrainStation * @Description: TODO 火车站类 * @Version 1.0 */ public class TrainStation implements SellTickets{ @Override public void sell() { System.out.println("在火车站进行卖票业务"); } }
代理类
package com.jsxs.structure.proxy.static_proxy; /** * @Author Jsxs * @Date 2023/4/19 13:42 * @PackageName:com.jsxs.structure.proxy.static_proxy * @ClassName: ProxyPoint * @Description: TODO 代售点 * @Version 1.0 */ public class ProxyPoint implements SellTickets{ private TrainStation trainStation=new TrainStation(); @Override public void sell() { System.out.println("代售点收取服务费帮火车站卖票,票的来源是火车站"); trainStation.sell(); } }
从上面的代码中我们可以看出测试类直接访问的是ProxyPoint类对象,也就是说ProxyPoint作为访问对象和目标对象的中介。同时也对sell方法进行了增强(代理点收取一些服务费用)
(4).JDK动态代理
我们使用上面的案例进行动态代理,先说说JDK提供的动态代理。Java中提供了一个动态代理类Proxy
,Proxy并不是我们上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法来获取对象
。
抽象主题类
package com.jsxs.structure.proxy.jdk_proxy; /** * @Author Jsxs * @Date 2023/4/19 13:40 * @PackageName:com.jsxs.structure.proxy.static_proxy * @ClassName: SellTickets * @Description: TODO 卖火车票的接口 * @Version 1.0 */ public interface SellTickets { void sell(); }
真实主题类
package com.jsxs.structure.proxy.jdk_proxy; /** * @Author Jsxs * @Date 2023/4/19 13:41 * @PackageName:com.jsxs.structure.proxy.static_proxy * @ClassName: TrainStation * @Description: TODO 火车站类 * @Version 1.0 */ public class TrainStation implements SellTickets { @Override public void sell() { System.out.println("在火车站进行卖票业务"); } }
JDK动态代理类
package com.jsxs.structure.proxy.jdk_proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @Author Jsxs * @Date 2023/4/19 14:04 * @PackageName:com.jsxs.structure.proxy.jdk_proxy * @ClassName: ProxyFactory * @Description: TODO 获取代理对象的工厂类 * @Version 1.0 */ public class ProxyFactory { // 1.声明目标对象 private TrainStation trainStation=new TrainStation(); // 2.获取代理对象的方法 public SellTickets getProxyObject(){ // 返回代理对象即可 /** * 1.类加载器: 用于加载代理类。可以通过目标对象获取类加载器 * 2.代理类实现的接口的字节码对象 * 3. 代理对象的调用处理程序: */ SellTickets proxyObject = (SellTickets)Proxy.newProxyInstance(trainStation.getClass().getClassLoader(), trainStation.getClass().getInterfaces(), new InvocationHandler() { /** * * @param proxy 代理对象: 和proxyObject对象是同一个对象,在invoke方法中基本不用 * @param method 对接口中的方法进行封装的method对象 * @param args 调用方法的参数 * @return 方法的返回值 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代售点收取一定的服务费用(JDK动态代理)..."); Object invoke = method.invoke(trainStation, args); return invoke; } }); return proxyObject; } }
客户端
package com.jsxs.structure.proxy.jdk_proxy; /** * @Author Jsxs * @Date 2023/4/19 14:18 * @PackageName:com.jsxs.structure.proxy.jdk_proxy * @ClassName: Client * @Description: TODO * @Version 1.0 */ public class Client { public static void main(String[] args) { // 1.创建代理类工厂 ProxyFactory proxyFactory = new ProxyFactory(); // 2.获取代理对象 SellTickets proxyObject = proxyFactory.getProxyObject(); // 3.调用售卖的方法 proxyObject.sell(); } }
使用动态代理,我们思考下面问题
- Proxy是代理类嘛?
ProxyFactory不是代理模式中所说的代理类
,而代理类是程序在运行过程中动态的在内存中生成的类。通过阿里巴巴开源的Java诊断工具(Arthas)查看代理类的结构。