《设计模式》之一文带你理解策略模式、原型模式(深浅拷贝)、观察者模式、装饰模式

我的github,到时上传例子代码
https://github.com/tihomcode


《设计模式》之一文带你理解单例、JDK动态代理、CGLIB动态代理、静态代理
《设计模式》之一文带你理解建造者模式、模板方法、适配器模式、外观模式


原型模式

什么是原型模式

原型模式是一个创建型的模式。原型二字表明了该模式应该有一个样板实例,用户从这个样板对象中复制一个内部属性一致的对象,这个过程也就是我们称的“克隆”。被复制的实例就是我们所称的“原型”,这个原型是可定制的。原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。(这里先用比较高深的术语说明,下面会有图解和白话方式表达)

克隆

原型模式的应用场景

1.类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。

2.通过new产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式。

3.一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。

4.Spring框架中的多例就是使用原型。

深拷贝和浅拷贝

创建Book类

public class Book implements Cloneable {

    //名称
    private String title;
    //图片
    private ArrayList<String> listImg = new ArrayList<String>();
    //页数
    private int pageNum;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public List<String> getListImg() {
        return listImg;
    }

    public void setListImg(ArrayList<String> listImg) {
        this.listImg = listImg;
    }

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public void addImg(String imgName) {
        listImg.add(imgName);
    }

    public void showBook() {
        System.out.println("-------------start---------------");
        System.out.println("title:"+title);
        for (String imgs : listImg) {
            System.out.println("img name:"+imgs);
        }
        System.out.println("pageNum:"+pageNum);
        System.out.println("-------------end-----------------");
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        super.clone();
    }
}

引用拷贝

Book book1 = new Book();
//设置属性
book1.setTitle("图书1");
book1.addImg("图片1");
book1.setPageNum(100);
//输出book1属性
book1.showBook();
//引用拷贝
Book book2 = book1;
//book2设置属性
book2.setTitle("图书2");
book2.addImg("图片2");
book2.setPageNum(200);
//输出book2属性
book2.showBook();
//输出book1属性
book1.showBook();
//打印地址
System.out.println(book1);
System.out.println(book2);
-------------start---------------
title:图书1
img name:图片1
pageNum:100
-------------end-----------------
-------------start---------------
title:图书2
img name:图片1
img name:图片2
pageNum:200
-------------end-----------------
-------------start---------------
title:图书2
img name:图片1
img name:图片2
pageNum:200
-------------end-----------------
com.tihom.prototype.Book@14ae5a5
com.tihom.prototype.Book@14ae5a5

在这里插入图片描述

从上图就可以看出来,引用拷贝后引用的还是同一个堆区创建的对象,如果book2修改属性的话,book1对应的属性也会被改变。

浅拷贝

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。”里面的对象“会在原来的对象和它的副本之间共享。

简而言之,浅拷贝只会拷贝基本类型,不会拷贝引用类型。

修改Book类中clone方法的实现

@Override
protected Object clone() throws CloneNotSupportedException {
	//默认浅克隆
    Book book = (Book) super.clone();
    return book;
}
Book book1 = new Book();
//设置属性
book1.setTitle("图书1");
book1.addImg("图片1");
book1.setPageNum(100);
//输出book1属性
book1.showBook();
//浅拷贝
Book book2 = (Book) book1.clone();
//book2设置属性
book2.setTitle("图书2");
book2.addImg("图片2");
book2.setPageNum(200);
//输出book2属性
book2.showBook();
//输出book1属性
book1.showBook();
//打印地址
System.out.println(book1);
System.out.println(book2);
-------------start---------------
title:图书1
img name:图片1
pageNum:100
-------------end-----------------
-------------start---------------
title:图书2
img name:图片1
img name:图片2
pageNum:200
-------------end-----------------
-------------start---------------
title:图书1
img name:图片1
img name:图片2
pageNum:100
-------------end-----------------
com.tihom.prototype.Book@14ae5a5
com.tihom.prototype.Book@7f31245a

在这里插入图片描述
在这里插入图片描述

分析:

ArrayList因为是引用类型,所以不会被拷贝,book1的arrayList和book2的arrayList依然引用的是同一个堆区的ArrayList对象,那么book2时的修改对book1同样产生影响;String是比较特殊的情况,因为String被final修饰,所以本身就不能被修改,一开始我们的理解它不是基本类型肯定是是不会被拷贝的,那么图书1的值应该是要变的,但是因为final特性,它不能被改变,因此出现了这样的输出;int是基本类型,所以它会被拷贝,那么图书2的值就与图书1的值无关,互不影响。

深拷贝

深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍,会在计算机中开辟一个新的内存地址。

修改clone的方法实现

@Override
protected Object clone() throws CloneNotSupportedException {
    //本来是浅拷贝,改为深拷贝
    Book book = (Book) super.clone();
    //将属性深拷贝
    book.listImg = (ArrayList<String>) this.listImg.clone();
    return book;
}

main方法不需要修改

-------------start---------------
title:图书1
img name:图片1
pageNum:100
-------------end-----------------
-------------start---------------
title:图书2
img name:图片1
img name:图片2
pageNum:200
-------------end-----------------
-------------start---------------
title:图书1
img name:图片1
pageNum:100
-------------end-----------------
com.tihom.prototype.Book@14ae5a5
com.tihom.prototype.Book@7f31245a

在这里插入图片描述
在这里插入图片描述

如图,印证了我上面阐述的观点

利用序列化实现深拷贝

// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);

oos.writeObject(this);

// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);

return ois.readObject();

单例里面用原型的话采用深拷贝,浅拷贝会产生线程安全问题

策略模式

什么是策略模式

定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

策略模式演示

/**
 * 策略模式定义抽象公共算法
 * @author TiHom
 * create at 2018/11/12 0012.
 */
abstract  class Strategy {

    public abstract void algorithmInterface();

}
/**
 * 初级会员 针对A算法
 * @author TiHom
 * create at 2018/11/12 0012.
 */
public class StrategyA extends Strategy{
    @Override
    public void algorithmInterface() {
        System.out.println("初级会员针对A算法");
    }
}

/**
 * 中级会员 针对B算法
 * @author TiHom
 * create at 2018/11/12 0012.
 */
public class StrategyB extends Strategy{
    @Override
    public void algorithmInterface() {
        System.out.println("中级会员针对B算法");
    }
}

/**
 * 高级会员 针对C算法
 * @author TiHom
 * create at 2018/11/12 0012.
 */
public class StrategyC extends Strategy{
    @Override
    public void algorithmInterface() {
        System.out.println("高级会员针对C算法");
    }
}
/**
 * 这里是上下文,对策略的二次封装
 * @author TiHom
 * create at 2018/11/12 0012.
 */
public class Context {

    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void algorithmInterface() {
        strategy.algorithmInterface();
    }
}
public class Context {

    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void algorithmInterface() {
        strategy.algorithmInterface();
    }
}
public class StrategyTest {

    public static void main(String[] args) {
        Context context = null;

        Strategy strategyA = new StrategyA();
        context = new Context(strategyA);
        context.algorithmInterface();

        Strategy strategyB = new StrategyB();
        context = new Context(strategyB);
        context.algorithmInterface();

        Strategy strategyC = new StrategyC();
        context = new Context(strategyC);
        context.algorithmInterface();
    }
}

Q:为什么不直接调用而要通过一层context来包装呢?

因为context是对策略进行二次封装,目的是避免高层模块对策略的直接调用,且可读性和扩展性提高.

Q:为什么使用抽象类而不使用接口?

抽象类最大的好处是能够实现继承,子父重写,且可以有普通方法的实现,而接口只能有方法,实现需要实现类去实现;且策略和模板方法、外观模式可以一起使用;

策略模式的应用场景

策略模式的用意是针对一组算法或逻辑,将每一个算法或逻辑封装到具有共同接口的独立的类中,从而使得它们之间可以相互替换。

策略模式使得算法或逻辑可以在不影响到客户端的情况下发生变化。说到策略模式就不得不提及OCP(Open Closed Principle) 开闭原则,即对扩展开放,对修改关闭。策略模式的出现很好地诠释了开闭原则,有效地减少了分支语句。

观察者模式

对于观察者模式我就不详细说明了,因为使用场景也挺多的,项目中开发经常使用到的MQ就是观察者模式的应用

什么是观察者模式

观察者模式(Observer),是一种行为性模型,行为型模式关注的是系统中对象之间的相互交互,解决系统在运行时对象之间的相互通信和协作,进一步明确对象的职责。相比来说,创建型模式关注对象的创建过程,结构型模式关注对象和类的组合关系。

观察者模式的应用场景

观察者模式主要用于1对N的通知。当一个对象的状态变化时,他需要及时告知一系列对象,令他们做出相应。

实现有两种方式:

推:每次都会把通知以广播的方式发送给所有观察者,所有的观察者只能被动接收。

拉:观察者只要知道有情况即可,至于什么时候获取内容,获取什么内容,都可以自主决定。

装饰模式

什么是装饰模式

装饰器模式,也成为包装模式,顾名思义,就是对已经存在的某些类进行装饰,以此来扩展一些功能。其结构图如下:
在这里插入图片描述

Component为统一接口,也是装饰类和被装饰类的基本类型。

ConcreteComponent为具体实现类,也是被装饰类,他本身是个具有一些功能的完整的类。

Decorator是装饰类,实现了Component接口的同时还在内部维护了一个ConcreteComponent的实例,并可以通过构造函数初始化。而Decorator本身,通常采用默认实现,他的存在仅仅是一个声明:我要生产出一些用于装饰的子类了。而其子类才是赋有具体装饰效果的装饰产品类。

ConcreteDecorator是具体的装饰产品类,每一种装饰产品都具有特定的装饰效果。可以通过构造器声明装饰哪种类型的ConcreteComponent,从而对其进行装饰。

结构图

在这里插入图片描述

装饰与代理区别

装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

装饰模式应用场景

在IO中,具体构件角色是节点流,装饰角色是过滤流。

FilterInputStream和FilterOutputStream是装饰角色,而其他派生自它们的类则是具体装饰角色。

DataOutputStream out=new DataOutputStream(new FileOutputStream());

这就是装饰者模式,DataOutputStream是装饰者子类,FileOutputStream是实现接口的子类。

这里不会调用到装饰者类–FilterOutputStream,只是作为继承的另一种方案,对客户端来说是透明的,是为了功能的扩张.

©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页