GOF23 创建型模式
单例模式
工厂模式
抽象工厂模式
建造者模式
原型模式
结构性模式
适配器模式
桥接模式
装饰模式
组合模式
外观模式
享元模式
代理模式
行为型模式
模板方法模式
命令模式
迭代器模式
观察者模式
中介者模式
备忘录模式
解释器模式
状态模式
策略模式
职责链模式
访问者模式
创建型模式 单例模式 饿汉单例模式 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.xiheya.single;public class Hungry { private byte [] data1 = new byte [1024 *1024 ]; private byte [] data2 = new byte [1024 *1024 ]; private byte [] data3 = new byte [1024 *1024 ]; private byte [] data4 = new byte [1024 *1024 ]; private Hungry () { } private final static Hungry HUNGRY = new Hungry (); public static Hungry getInstance () { return HUNGRY; } }
存在的问题 可能会浪费空间,尽管对象空间没有被使用,但是还是会被开辟出来。
懒汉式单例 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package com.xiheya.single;public class LazyMan { private LazyMan () { } private volatile static LazyMan lazyMan; public static LazyMan getInstance () { if (lazyMan == null ){ synchronized (LazyMan.class){ if (lazyMan == null ){ lazyMan = new LazyMan (); } } } return lazyMan; } }
注意点
懒汉模式对象如果不加volatile就会出现指令重排。
而我们要保证其多线程安全,就需要加上双重锁
1 2 3 4 synchronized (LazyMan.class){ if (lazyMan == null ){ lazyMan = new LazyMan (); }
静态内部类 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.xiheya.single;public class Holder { private Holder () { } public static Holder getInstance () { return InnerClass.HOLDER; } public static class InnerClass { private static final Holder HOLDER = new Holder (); } }
单例不安全,因为有反射。反射会破坏单例
所以为了解决这个问题我们使用枚举来实现单例模式。
枚举单例模式 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 package com.xiheya.single;public enum EnumSingle { INSTANCE; public EnumSingle getInstance () { return INSTANCE; } }
因为反射不能破解枚举,所以我们使用枚举来实现单例模式。
工厂模式 作用
实现了创建者和调用者的分离
详细分类:
OOP七大原则
开闭原则:一个软件的实体应当对扩展开放,对修改关闭。
依赖倒转原则:要针对接口编程,不要针对实现编程。
迪米特法则:只与你直接的朋友通信,而避免和陌生人通信。
核心本质
实例化对象不适用new,用工厂方法代替
将选择实现类,创建对象统一管理和控制,从而将调用者跟我们的实现类解耦
三种模式:
简单工厂模式
用来生产同一等级结构中的任意产品(对于增加新的产品,需要球盖已有代码)
工厂方法模式
用来生产同一等级结构中的固定产品(支持增加任意产品)
抽象工厂模式
围绕一个超级工厂创建其他工厂。该工厂又称为其他工厂的工厂。
简单工厂模式 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package com.xiheya.factory.simple;public class Consumer { public static void main (String[] args) { Car car = CarFactory.getCar("五菱" ); Car car1 = CarFactory.getCar("特斯拉" ); car.name(); car1.name(); } }
弊端
未实现开闭原则
实现结果
流程图
工厂方法模式 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 package com.xiheya.factory.method;import com.xiheya.factory.simple.CarFactory;public class Consumer { public static void main (String[] args) { Car car = new TeslaFactory ().getCar(); car.name(); Car car1 = new WuLingFactory ().getCar(); car1.name(); Car car2 = new DaZhongFactory ().getCar(); car2.name(); } }
弊端
虽然实现了代码的开闭原则,并且可以动态拓展。但是工厂方法模式实现成本太高了。
实现结果
流程图
小结
简单工厂模式(静态工厂模式)
工厂方法模式
抽象工厂模式
应用场景
JDK中的Calendar的getInstance方法
JDBC中的Connection对象的获取
Spring中IOC容器创建管理bean对象
反射中Class对象的newInstance方法
抽象工厂模式 定义
定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们的类
适用场景
客户端(应用层)不依赖于产品类实例如何被创建、实现的细节
强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码。
提供一个产品类的库,所有产品以同样的接口出现,从而使得客户端不依赖于具体的实现
优点
具体产品在应用层的代码隔离,无需关心创建的细节
将一个系列的产品统一到一起创建
缺点
规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
增加了系统的抽象性和理解难度。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 package com.xiheya.factory.abstract1;public class Client { public static void main (String[] args) { System.out.println("==============小米系列产品================" ); XiaomiFactory xiaomiFactory = new XiaomiFactory (); IPhoneProduct xiaomi = xiaomiFactory.iphoneProduct(); xiaomi.callup(); xiaomi.sendSMS(); IRouterProduct xiaomiRouter = xiaomiFactory.irouterProduct(); xiaomiRouter.openWifi(); xiaomiRouter.setting(); System.out.println("==============华为系列产品================" ); HuaweiFactory huaweiFactory = new HuaweiFactory (); IPhoneProduct huawei = huaweiFactory.iphoneProduct(); huawei.callup(); huawei.sendSMS(); IRouterProduct huaweiRouter = huaweiFactory.irouterProduct(); huaweiRouter.openWifi(); huaweiRouter.setting(); } }
运行结果
流程图
建造者模式 定义
建造者模式也属于创建类模式,它提供了一种创建对象的最佳方式。
它将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。
主要作用 在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。
用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
例子:
工厂(建造者模式):负责制造汽车(组装过程和细节在工厂内)
汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车是怎么组装的(车轮、车门、发动机、方向盘等等))
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 package com.design_patterns.builder;public class Test { public static void main (String[] args) { Director director = new Director (); Product build = director.build(new Worker ()); System.out.println(build.toString()); } }
运行结果
上面示例是Builder模式的常规用法,导演类Director在Builder模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用着返回完整的产品类,但是有些情况下需要简化系统结构,可以把Director和抽象建造者进行结合。
通过静态内部类方式实现零件无序装配构造,这种方式使用更加灵活,更符合定义。内部有复杂对象的默认实现,使用时可以根据用户需求自由定义更改内容,并且无需改变具体的构造方式。就可以生产出不同复杂产品。
比如:麦当劳的套餐,服务员(具体建造者)可以随意搭配任意几种产品(零件)组成一款套餐(产品),然后出售给客户。彼地种方式少了指挥者,主要是因为第二种方式把指挥者交给用户来操作,使得产品的创建更加简单灵活。
无指挥类的建造者模式 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 package com.design_patterns.builder.demo02;public class Test { public static void main (String[] args) { Worker worker = new Worker (); Product product = worker.buildA("炸鸡" ).buildB("芬达" ).getProduct(); System.out.println(product.toString()); } }
运行结果
优缺点 优点
产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节。
将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰.
具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合“开闭原则”
缺点
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其适用范围受到一定的限制。
如果产品的内部变化复杂,可能会导致需要定义很多具体建造类来实现这种变化,导致系统变得很庞大。
应用场景
需要生产的产品对象有复杂的内部结构,这些产品对象具有共性。
隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品、
适合于一个具有较多的零件(属性)的产品(对象)的创建过程。
建造者与抽象工厂模式的比较
与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
如果将抽象工厂模式堪称汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。
原型模式 实现步骤
实现一个接口:Cloneable;
重写一个方法:clone();
主要用于:Spring Bean:单例模式,原型模式
原型模式+工厂模式 ===> new <=> 原型模式
浅克隆模式 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 package com.design_patterns.prototype.demo01;import java.util.Date;public class Bilibili { public static void main (String[] args) throws CloneNotSupportedException { Date date = new Date (); Video v1 = new Video ("xiheya" ,date); Video v2 = (Video) v1.clone(); System.out.println("v1-->" + v1); System.out.println("v1:hashcode-->" + v1.hashCode()); System.out.println("v2-->" + v2); System.out.println("v2:hashcode-->" + v2.hashCode()); System.out.println("====================================" ); date.setTime(123456 ); System.out.println("v1-->" + v1); System.out.println("v1:hashcode-->" + v1.hashCode()); System.out.println("v2-->" + v2); System.out.println("v2:hashcode-->" + v2.hashCode()); System.out.println("====================================" ); } }
结果
深克隆模式 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 package com.design_patterns.prototype.demo02;import java.util.Date;public class Bilibili { public static void main (String[] args) throws CloneNotSupportedException { Date date = new Date (); Video v1 = new Video ("xiheya" ,date); Video v2 = (Video) v1.clone(); System.out.println("v1-->" + v1); System.out.println("v1:hashcode-->" + v1.hashCode()); System.out.println("v2-->" + v2); System.out.println("v2:hashcode-->" + v2.hashCode()); System.out.println("====================================" ); date.setTime(123456 ); System.out.println("v1-->" + v1); System.out.println("v1:hashcode-->" + v1.hashCode()); System.out.println("v2-->" + v2); System.out.println("v2:hashcode-->" + v2.hashCode()); System.out.println("====================================" ); } }
结果
浅克隆与深克隆对比
浅克隆:克隆出来的对象和原型共同指向一个对象,克隆对象只是引用了这个对象。
深克隆:克隆出来的对象会把原型的属性也克隆出来。