大话设计模式 笔记2

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010588262/article/details/81611165

笔记1

备忘录模式

这是一个有着明确使用场景的模式,要保存,恢复一个类的状态时使用,例如游戏存档,编辑器undo,redo等等。
涉及到三个类:
Originator 原发器:主类,需要保存并恢复其状态的类
Memento 备忘录:用于记录发起者某一时刻的状态
Caretaker 管理者:用于保存备忘录

发起者里面会有保存方法,返回一个Memento,还有一个恢复方法,根据参数Memento来恢复自身状态,这两个类很好理解。书里没有对Caretaker提及太多,而且给的示例中只用Caretaker存储一个Memento,让我觉得这个类没啥作用,反而违反了迪米特法则让客户端多认识一个类,查阅资料后有一篇文章说的让我理解了:

忘录模式中并不一定需要一个负责人对象。广义来说,调用发起人角色获得备忘录对象后,备忘录放在哪里,那个对象就是管理者对象。
负责人对象并不是只管理一个备忘录对象,它可以管理多个备忘录对象。
狭义的负责人只管理同一类的备忘录对象,但广义的管理者可以管理不同类型的备忘录对象。
负责人对象需要实现的基本功能是:存入备忘录对象和从中获取备忘录对象。从功能上看,就是一个缓存功能或一个简单的对象实例池。
负责人角色虽然能存取备忘录对象,但是不能访问备忘录对象的内部数据。

很多地方强调了Memento类的内容只能被Originator访问,即原发器与备忘录对象之间建立的是一个宽接口。原发器能够看到一个宽接口,允许它访问所需的所有数据,来返回先前的状态。通常实现成为原发器内的一个私有内部类。管理者只是用来保存备忘录对象,管理者对象并不是只管理一个备忘录对象,它可以管理多个备忘录对象。管理者只能看到备忘录的窄接口,这个接口的实现通常没有任何的方法,只是一个类型标识。窄接口使得管理者只能将备忘录传递给其他对象。

组合模式

用来让客户端无差别地对待的每一个节点,方便添加、删除、遍历节点等操作。将树的节点定义为父类,包含所有节点通用的方法,然后各个类型的节点都继承这个类

迭代器模式

很多主流语言里都把迭代器内置了,很多时候程序猿不用自己实现迭代器,因此主要是理解这个模式的设计思路。
迭代方法被迭代的对象分离解耦
例如我现在有个Company类,里面有个List<Employee> employees,我要遍历里面的员工,没有迭代器模式那我要直接在客户端:

for(int i=0;i<company.employees.size();i++){
    company.employees.get(i);
}

怎么说呢,看起来一点也不高大上,而且不符合迪米特法则,把company内部的employees对象暴露了,而且如果现在要改成逆序遍历,改动也不简洁,如果多个地方要改,就更崩溃。

所以创建一个迭代器接口Iterator,里面包括了迭代的通用方法:boolean hasNext()以及Object next()
再创建实现类ConcreteIterator

class ConcreteIterator implements Iterator{
    private List list = new ArrayList();
    private int cursor =0;
    public ConcreteIterator(List list){
        this.list = list;
    }
    public boolean hasNext() {
        if(cursor==list.size()){
            return false;
        }
        return true;
    }
    public Object next() {
        Object obj = null;
        if(this.hasNext()){
            obj = this.list.get(cursor++);
        }
        return obj;
    }
}

可以再来一个逆序迭代器:

class DescConcreteIterator implements Iterator{
    private List list = new ArrayList();
    private int cursor =0;
    public ConcreteIterator(List list){
        this.list = list;
        cursor = list.size()-1;
    }
    public boolean hasNext() {
        if(cursor==0){
            return false;
        }
        return true;
    }
    public Object next() {
        Object obj = null;
        if(this.hasNext()){
            obj = this.list.get(cursor--);
        }
        return obj;
    }
}

然后是需要迭代的类的接口了:

interface Aggregate {
    public void add(Object obj);
    public void remove(Object obj);
    public Iterator iterator();
}

实现类:

class ConcreteAggregate implements Aggregate {
    private List list = new ArrayList();
    public void add(Object obj) {
        list.add(obj);
    }

    public Iterator iterator() {
        return new ConcreteIterator(list);
    }

    public Iterator descIterator() {
        return new DescConcreteIterator(list);
    }

    public void remove(Object obj) {
        list.remove(obj);
    }
}

客户端:

public static void main(String[] args){
        Aggregate ag = new ConcreteAggregate();
        ag.add("1");
        ag.add("2");
        ag.add("3");
        Iterator it = ag.iterator();
        while(it.hasNext()){
            String str = (String)it.next();
            System.out.println(str);
        }
    }

想要变成逆排序?Iterator it = ag.descIterator();

单例模式

目标是整个运行过程中某各类只能产生一个实例,以下方法中除了枚举以外都是不能防止反射的

饿汉
程序一启动就实例化对象,浪费资源

public class SingleClass {

    private SingleClass single = new SingleClass();

    private SingleClass() {
    }

    public SingleClass instance() {
        return single;
    }
}

懒汉
在第一次使用时才会实例化对象

public class SingleClass {

    private SingleClass single = null;

    private SingleClass() {
    }

    public SingleClass instance() {
        if (single == null)
            single = new SingleClass();
        return single;
    }
}

高并发时,两个线程同时执行instance()方法并判断single为null,就会创建两个实例

双重锁判断

public class SingleClass {

    private SingleClass single = null;

    private SingleClass() {
    }

    public SingleClass instance() {
        // 第一次判断
        if (single == null) {
            synchronized (single) {
                // 第二次判断
                if (single == null)
                    single = new SingleClass();
            }
        }
        return single;
    }
}

把整个方法变成同步的可以确保单例,但是性能太差,所以出现这种方式,第一个if判断确保只要single对象实例完成,后面就不用进行锁判断了。第二个if作用是,当一开始single没有实例的时候,多个线程同时通过了第一层判断,虽然第二层加了锁,但是如果锁里面不加判断,那么这几个线程依然会依次完成实例化,也就是产生多个实例了。

枚举

静态内部类

public class SingleClass {

    private static class SingleClassHolder {
        public static final SingleClass INSTANCE = new SingleClass();
    }

    private SingleClass() {
    }

    public SingleClass instance() {
        return SingleClassHolder.INSTANCE;
    }
}

同样也是延迟加载,当第一次使用SingleClassHolder时才会加载内部类并初始化静态变量,并由虚拟机来保证它的线程安全性

容器

public class SingletonManager {
    private static Map<String,Object> map=new HashMap<String, Object>();

    private SingletonManager(){}

    public static void registerService(String key,Object instance){
        if (!map.containsKey(key)){
            map.put(key,instance);
        }
    }

    public static Object getService(String key){
        return map.get(key);
    } 
}

简单工厂,网上搜索到的容器实现感觉没有考虑到多线程情况

桥接模式

优先考虑聚合组成关系,再考虑继承
聚合:生命周期不一致,例如:雁群和大雁
组成:生命周期一致,强依赖,例如:大雁和翅膀

职责链模式

此模式很好理解也很常用
例如办公场景,一个申请要一层一层向上传递
或者是tomcat的过滤器链
每个处理者保留下一级别的引用,当请求发到自己这里时,要么处理,要么推给下一级

中介者模式

比如一个多人聊天场景,ABCDE每个人都是一个实例,A要跟BCDE聊天,他得维护自己的好友列表,然后发送消息时自己找到那个人直接发给他,这样是一种网状关系,随着好友人数变多,网变得越来越复杂,每个实例知道的信息越来越多,不符合迪米特法则。
此时我们引入中介者,所有的关系维护在中介者类中,任何消息都发送给中介者,告诉他消息内容和消息接受者即可。
此模式最大的好处是减少引用数量。

享元模式

关键词:享元工厂 内部状态 外部状态 减少对象数量(还记得不,中介者模式可以减少引用数量)

参考:https://www.zybuluo.com/coder-pig/note/653571

使用此模式,首先将对象里的属性分为内部状态和外部状态,例如扑克牌对象,有花色数值两个属性,有Show()方法打印此张牌的花色和数值信息,如果不做区分,那么要达到展示一整副牌的目的,就要new出52(花色数量*数值数量)个对象,遍历调用Show()方法
如果把花色保留原样,作为内部状态,数值改为外部状态,作为参数传入Show方法,那就只需要4个对象即可,分别调用12次Show方法
有人会问了,如果把两个属性都作为外部参数呢,那只需要一个对象了不是更省内存?
那咱还要啥面向对象呢是吧,而且这个只是最简单的例子,卡牌类中没有别的方法,如果有其他方法要用到花色属性呢?所以如何划分内部外部属性是需要考虑的
享元工厂是做啥的呢?就是用来生产对象的,它维护了Map<内部属性,对象>,创建对象时给工厂内部属性,已经存在的直接返回旧的对象,否则新建一个对象

猜你喜欢

转载自blog.csdn.net/u010588262/article/details/81611165
今日推荐