目录
1、什么是设计模式
设计模式(英语 design pattern)是对面向对象设计中反复出现的问题的解决方案。(注意:设计模式是针对业务需求所设置的模式,并不是为了设计模式而设计模式)
用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
//开发中的陷阱
public class Demo1 {
public static void main(String[] args) {
HashMap hm1 = new HashMap();
hm1.put("name","zs");
hm1.put("sex","女");
HashMap hm2 = hm1;
hm1.put("age","18");
hm2.put("like","男");
System.out.println(hm1);
System.out.println(hm2);
}
public static void main(String[] args) {
HashMap hm1 = new HashMap();
hm1.put("name","zs");
hm1.put("sex","女");
HashMap hm2 = (HashMap) hm1.clone();
hm1.put("age","18");
hm2.put("like","男");
System.out.println(hm1);
System.out.println(hm2);
}
}
原型模式(Prototype)
prototype:原型
clone:克隆
需求:将一只名字为杰克、性别为母的绵羊克隆10份;
要求每只绵羊的属性、性别都一致;
- 使用前
```
package com.javaxl.design.prototype.before;
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 10:45
*/
public class Sheep {
private String name;
private String sex;
public Sheep(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
package com.javaxl.design.prototype.before;
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 10:46
*
* 将一只名字为杰克、性别为母的绵羊克隆10份;
* 要求每只绵羊的属性、性别都一致;
*
* 弊端:无法将当前的状态进行复制
*/
public class Client {
public static void main(String[] args) {
Sheep sheep1 = new Sheep("杰西", "母");
Sheep sheep2 = new Sheep("杰西", "母");
Sheep sheep3 = new Sheep("杰西", "母");
Sheep sheep4 = new Sheep("杰西", "母");
Sheep sheep5 = new Sheep("杰西", "母");
Sheep sheep6 = new Sheep("杰西", "母");
Sheep sheep7 = new Sheep("杰西", "母");
Sheep sheep8 = new Sheep("杰西", "母");
Sheep sheep9 = new Sheep("杰西", "母");
Sheep sheep10 = new Sheep("杰西", "母");
// 此时我要一只名为杰瑞的绵羊,其它绵羊属性与杰西一致;
// 那么按照这种设计,只能这么创建所需的绵羊
// 这种方式创建,目前只有两个属性问题不大,如果绵羊类有十几二十甚至更多的属性,那么是非常不方便的
Sheep sheep11 = new Sheep("杰瑞", "母");
}
}
```
- 使用后
```
package com.javaxl.design.prototype.after;
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 10:45
*
* 使用原型设计模式进行设计
*/
public class Sheep implements Cloneable{
private String name;
private String sex;
public Sheep(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
System.out.println("被克隆了...");
return obj;
}
}
package com.javaxl.design.prototype.after;
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 10:46
*
* 将一只名字为杰克、性别为母的绵羊克隆10份;
* 要求每只绵羊的属性、性别都一致;
*
* 使用原型设计模式进行设计后的测试
*/
public class Client {
public static void main(String[] args) throws Exception{
Sheep sheep1 = new Sheep("杰西", "母");
Sheep sheep2 = (Sheep) sheep1.clone();
Sheep sheep3 = (Sheep) sheep1.clone();
Sheep sheep4 = (Sheep) sheep1.clone();
Sheep sheep5 = (Sheep) sheep1.clone();
Sheep sheep6 = (Sheep) sheep1.clone();
Sheep sheep7 = (Sheep) sheep1.clone();
Sheep sheep8 = (Sheep) sheep1.clone();
Sheep sheep9 = (Sheep) sheep1.clone();
Sheep sheep10 = (Sheep) sheep1.clone();
System.out.println(sheep1);
System.out.println(sheep2);
// 此时我要一只名为杰瑞的绵羊,其它绵羊属性与杰西一致;
// 按照原型设计模式,调用方Client类无需查找杰西相同部分的属性,只需变动差异部分属性进行克隆即可;
// 这种设计,目前只有两个属性使用起来感觉没多大区别,如果绵羊类有十几二十甚至更多的属性,那么感觉非常明显
sheep1.setName("杰瑞");//其它的属性不需要去关注
Sheep sheep11 = (Sheep) sheep1.clone();
System.out.println(sheep11);
}
}
```
结论:从对象创建的角度上来说,原型模式设计让相似的类实例创建更加的便捷
深层次的使用
- 浅拷贝
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 10:45
*
* 使用原型设计模式进行设计
*/
public class Sheep implements Cloneable{
private String name;
private String sex;
private Sheep friend;
public Sheep(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", friend=" + friend +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
System.out.println("被克隆了...");
return obj;
}
}
```
浅拷贝特点:对象中实例变量,如果是引用变量,不会重新开辟空间
- 深拷贝
- 方式一
```
package com.javaxl.design.prototype.deep.one;
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 10:45
* <p>
* 使用原型设计模式进行设计
*/
public class Sheep implements Cloneable {
private String name;
private String sex;
private Sheep friend;
private Sheep boyFriend;
public Sheep(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
public Sheep getBoyFriend() {
return boyFriend;
}
public void setBoyFriend(Sheep boyFriend) {
this.boyFriend = boyFriend;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", friend=" + friend +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
if (obj == null) {
return obj;
} else {
// 被克隆出来的绵羊(目前这只绵羊的朋友还是本体的朋友)
Sheep sheep = (Sheep) obj;
// 将本体的朋友也克隆一份出来,给克隆羊
Sheep friend = this.getFriend();
if (friend !=null){
sheep.setFriend((Sheep) friend.clone());
}
return sheep;
}
}
}
package com.javaxl.design.prototype.deep.one;
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 10:46
* <p>
* 将一只名字为杰克、性别为母的绵羊克隆10份;
* 要求每只绵羊的属性、性别都一致;
* <p>
* 使用原型设计模式进行设计后的测试
*/
public class Client {
public static void main(String[] args) throws Exception {
Sheep sheep1 = new Sheep("杰西", "母");
Sheep friend_jiexi = new Sheep("杰西的朋友", "公");
Sheep boyFriend_jiexi = new Sheep("杰西的男朋友", "公");
sheep1.setFriend(friend_jiexi);
sheep1.setBoyFriend(boyFriend_jiexi);
Sheep sheep2 = (Sheep) sheep1.clone();
System.out.println("第1只叫杰西的绵羊的朋友:" + sheep1.getFriend().hashCode());
System.out.println("第2只叫杰西的绵羊的朋友:" + sheep2.getFriend().hashCode());
System.out.println("第1只叫杰西的绵羊的男朋友:" + sheep1.getBoyFriend().hashCode());
System.out.println("第2只叫杰西的绵羊的男朋友:" + sheep2.getBoyFriend().hashCode());
}
}
```
![](创建型模式.assets/1582343860268-1582641669694.png)
- 方式二
```
package com.javaxl.design.prototype.deep.two;
import java.io.*;
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 10:45
*
* 使用原型设计模式进行设计
*/
public class Sheep implements Cloneable,Serializable{
private String name;
private String sex;
private Sheep friend;
private Sheep boyFriend;
public Sheep getBoyFriend() {
return boyFriend;
}
public void setBoyFriend(Sheep boyFriend) {
this.boyFriend = boyFriend;
}
public Sheep(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", friend=" + friend +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
System.out.println("被克隆了...");
return obj;
}
protected Object deepClone() throws CloneNotSupportedException, IOException, ClassNotFoundException {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// 获取对象输出流
ObjectOutputStream oos = new ObjectOutputStream(bos);
// 将当前的对象
oos.writeObject(this);
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
Object obj = ois.readObject();
return obj;
}
}
public class Client {
public static void main(String[] args) throws Exception {
Sheep sheep1 = new Sheep("杰西", "母");
Sheep friend_jiexi = new Sheep("杰西的朋友", "公");
Sheep boyFriend_jiexi = new Sheep("杰西的男朋友", "公");
sheep1.setFriend(friend_jiexi);
sheep1.setBoyFriend(boyFriend_jiexi);
Sheep sheep2 = (Sheep) sheep1.deepClone();
System.out.println("第1只叫杰西的绵羊的朋友:" + sheep1.getFriend().hashCode());
System.out.println("第2只叫杰西的绵羊的朋友:" + sheep2.getFriend().hashCode());
System.out.println("第1只叫杰西的绵羊的男朋友:" + sheep1.getBoyFriend().hashCode());
System.out.println("第2只叫杰西的绵羊的男朋友:" + sheep2.getBoyFriend().hashCode());
}
}
```
结论:推荐使用深拷贝的第二种方式
注意事项和细节
- 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
- 不用重新初始化对象,而是动态地获得对象运行时的状态
- 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码缺点:
1、需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则
2、实现深克隆的时候可能需要比较复杂的代码
2、创建型模式
一、Singleton,单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问点
二、Abstract Factory,抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类。
三、Factory Method,工厂方法:定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到了子类。
四、Builder,建造模式:将一个复杂对象的构建与他的表示相分离,使得同样的构建过程可以创建不同的表示。
五、Prototype,原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。
单例:Singleton
- 分类
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步代码块)
- 懒汉式(线程安全,同步方法)
- 双重检查
- 静态内部类
- 枚举
- 饿汉式(静态常量)
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-21 17:09
* 饿汉式(静态常量)
*/
public class DBAccess {
// 构造器私有化,避免外部创建对象
private DBAccess() {
}
// static修饰,保障其能够被静态方法访问
private final static DBAccess dbAccess = new DBAccess();
// 外部直接调用静态方法实例化对象
public static DBAccess getInstance() {
return dbAccess;
}
}
饿汉式(静态代码块)
class DBAccess2 {
private DBAccess2() {
}
private static DBAccess2 dbAccess = null;
static {
dbAccess = new DBAccess2();
}
public static DBAccess2 getInstance() {
return dbAccess;
}
}
懒汉式(线程不安全)
class DBAccess3 {
private DBAccess3() {
}
private static DBAccess3 dbAccess = null;
public static DBAccess3 getInstance() {
if (dbAccess == null) {
dbAccess = new DBAccess3();
}
return dbAccess;
}
}
懒汉式(同步代码块)
class DBAccess4 {
private DBAccess4() {
}
private static DBAccess4 dbAccess = null;
public static DBAccess4 getInstance() {
synchronized (DBAccess4.class) {
if (dbAccess == null) {
dbAccess = new DBAccess4();
}
}
return dbAccess;
}
}
懒汉式(线程安全,同步方法)
class DBAccess5 {
private DBAccess5() {
}
private static DBAccess5 dbAccess = null;
public synchronized static DBAccess5 getInstance() {
if (dbAccess == null) {
dbAccess = new DBAccess5();
}
return dbAccess;
}
}
双重检查
class DBAccess6 {
private DBAccess6() {
}
private static DBAccess6 dbAccess = null;
public static DBAccess6 getInstance() {
if (dbAccess == null) {
synchronized (DBAccess6.class) {
if (dbAccess == null) {
dbAccess = new DBAccess6();
}
}
}
return dbAccess;
// return new DBAccess6();
}
}
静态内部类
class DBAccess7 {
private DBAccess7() {
}
private static class DBAccess7Instance{
private static DBAccess7 dbAccess = new DBAccess7();
}
public static DBAccess7 getInstance() {
return DBAccess7Instance.dbAccess;
}
}
枚举
enum DBAccess8{
DBACCESS;
public static DBAccess8 getInstance() {
return DBAccess8.DBACCESS;
}
}
结论:
单例中两种饿汉式可用,但是存在性能问题
单例中三种懒汉式不推荐,存在线程安全问题,同步方法的方式解决了线程的问题,但是性能极差
最后三种单例模式值得推荐
- 注意事项
- 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new
3、结构模式
六、Iterator,迭代器模式:提供一个方法顺序访问一个聚合对象的各个元素,而又不需要暴露该对象的内部表示。
七、Observer,观察者模式:定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知自动更新。
八、Template Method,模板方法:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,TemplateMethod使得子类可以不改变一个算法的结构即可以重定义该算法得某些特定步骤。
九、Command,命令模式:将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队和记录请求日志,以及支持可撤销的操作。
十、State,状态模式:允许对象在其内部状态改变时改变他的行为。对象看起来似乎改变了他的类。
十一、Strategy,策略模式:定义一系列的算法,把他们一个个封装起来,并使他们可以互相替换,本模式使得算法可以独立于使用它们的客户。
十二、China of Responsibility,职责链模式:使多个对象都有机会处理请求,从而避免请求的送发者和接收者之间的耦合关系
十三、Mediator,中介者模式:用一个中介对象封装一些列的对象交互。
十四、Visitor,访问者模式:表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这个元素的新操作。
十五、Interpreter,解释器模式:给定一个语言,定义他的文法的一个表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
十六、Memento,备忘录模式:在不破坏对象的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
4、行为模式
十七、Composite,组合模式:将对象组合成树形结构以表示部分整体的关系,Composite使得用户对单个对象和组合对象的使用具有一致性。
十八、Facade,外观模式:为子系统中的一组接口提供一致的界面,fa?ade提供了一高层接口,这个接口使得子系统更容易使用。
十九、Proxy,代理模式:为其他对象提供一种代理以控制对这个对象的访问
二十、Adapter,适配器模式:将一类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作那些类可以一起工作。
二十一、Decrator,装饰模式:动态地给一个对象增加一些额外的职责,就增加的功能来说,Decorator模式相比生成子类更加灵活。
二十二、Bridge,桥模式:将抽象部分与它的实现部分相分离,使他们可以独立的变化。
二十三、Flyweight,享元模式