设计模式干货

在这里插入图片描述
前言:
设计模式描述的是类与类之间的关系.常常可以和真实世界模型对比
需要code基础,最终都需要code实现。23中设计模式,核心就是看23个小程序.
需要的uml:类图:属性(变量)+操作; 类图关系:继承,聚合(组合),依赖(使用);
notes:聚合与组合区别:举例:树和树叶是组合关系,一旦树枯死了,树叶也就不能存活了;车和车轮是聚合关系,车轮就算没有安装在汽车上一样可以存在.
0 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、 命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
创建模式解决的是对象创建以及存在方式相关设计问题的,例如单例,工厂等,而结构模式主要解决的是类之间结构化关系的,
行为模式不仅描述对象或类的模式,还描述它们之间的通信模式。行为对象模式使用对象复合而不是继承
例如代理、装饰、共享、门面(外观)等,
行为模式则是解决类之间调用关系(行为)的。
在这里插入图片描述
notes: 这个blog会继续更新,补充如果不用设计模式如何实现和设计模式对应的真实世界.
1创建者型模式
1.1 单例模式 难度* 使用*****
关键字:不许new,static唯一
分成两种饿汉式单例
1.1.1 饿汉式单例和懒汉式单例

public class MySingleton {
    private static MySingleton instance = new MySingleton();
    private MySingleton(){}
    public static MySingleton getInstance() {
        return instance;
    }
}

1.1.2 懒汉式单例

public class MySingleton {
    private static MySingleton instance = null;
    private MySingleton(){}
    public static MySingleton getInstance() {
        if(instance == null){//懒汉式
            instance = new MySingleton();
        }
        return instance;
    }
}

notes:饿汉式单例多线程没有问题. 懒汉式单例因为 if(instance == null)多线程会错误,两种解决方案,1)加同步锁缺点效率低 2 ) 加双重保护,详见https://zhuanlan.zhihu.com/p/114561062
1.2 3种工厂模式
1.2.1 简单工厂模式 难度系数 * 应用系数***
定义:根据传递的参数不同,返回不同类的实例。通俗的说就是switch type,然后 new一下模板模式.
工厂模式:分成两个class,第一个class 是种类, 第二个class 是具体执行的操作.
第一个class就是简单工厂, 两者是1对1的关系。
关键字:factory(type)。 按照一个类即种类划分工厂.
应用场景: 同一个抽象类的子类. 根据type不同,new 不同对象.
关键代码:

/* * 专门用于创建披萨的工厂类 */
public class SimplePizzaFactory {
    public Pizza createPizza(String type){
        Pizza pizza = null;
        if(type.equals("cheese")){
            pizza = new CheesePizza();
        }
        else if(type.equals("clam")){
            pizza = new ClamPizza();
        }
        else if(type.equals("pepperoni")){
            pizza = new PepperoniPizza();
        }
        return pizza;
    }
}

1.2.2 工厂模式 难度系数 ** 应用系数***
定义: 工厂模式又叫做虚拟构造子(Virtual Constructor)模式或者多态性工厂(Polymorphic Factory)模式。
工厂方法模式的用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。
通俗的说工厂模式就是多态。C + +中的工厂方法都是虚函数并且常常是纯虚函数
关键字:factory 多态
关键代码

public class NYStyleCheesePizza extends Pizza{
...
}
public class NYStyleCheesePizza extends Pizza{
...
}

1.2.3 抽象工厂模式 难度系数 *** 应用系数***
可以看完策略模式之后,在看一遍抽象工厂模式
抽象工厂class is N*N 关系. 记得下面的uml依赖关系(每个class1使用自己的class2)就记得抽象工厂了.
在这里插入图片描述
2种class,比如多种工厂和多种产品.比如 intel computer ,amd computer(computer由cpu,内存组成)
关键代码

private void prepareHardwares(int cpuType , int mainboard){
        //这里要去准备CPU和主板的具体实现,为了示例简单,这里只准备这两个
        //可是,装机工程师并不知道如何去创建,怎么办呢?       
        //直接找相应的工厂获取
        this.cpu = CpuFactory.createCpu(cpuType);
        this.mainboard = MainboardFactory.createMainboard(mainboard); 
        //测试配件是否好用
        this.cpu.calculate();
        this.mainboard.installCPU();
}
public class CpuFactory {
    public static Cpu createCpu(int type){
        Cpu cpu = null;
        if(type == 1){
            cpu = new IntelCpu(755);
        }else if(type == 2){
            cpu = new AmdCpu(938);
        }
        return cpu;
    }
}
public class MainboardFactory {
    public static Mainboard createMainboard(int type){
        Mainboard mainboard = null;
        if(type == 1){
            mainboard = new IntelMainboard(755);
        }else if(type == 2){
            mainboard = new AmdMainboard(938);
        }
        return mainboard;
    }
}

没有采用简单工厂和工厂模式的类似例子,因为抽象工厂uml图不直观。
https://www.cnblogs.com/chenssy/archive/2013/06/03/3114681.html
例子中芝加哥和纽约一组class1,另一组是Dough ,Sauce ,Cheese ,Veggies,Clams。应该是后者5个选几个配置一种形成class2,与class1匹配。但是图中采用的是细化每种原料,比如Cheese ,分成Cheese 1,Cheese 2。等于组2的子类不同形成class2,造成复杂化.
1.2.4 简单工厂模式 vs. 工厂模式 vs. 抽象工厂模式
简单工厂和工厂模式区别?
简单工厂没有多个子类extends 抽象类
工厂模式和抽象工厂模式区别?
前者1vs n,后者n vs n
1.2.5 工厂模式和其他模式的区别?
模板模式和工厂模式区别?
使用模板模式来创建实例, 就是工厂模式
继承就是模板模式和工厂模式,
策略模式和工厂模式区别?
策略模式先set 策略后返回某个对象。工厂模式是new出来一个对象。后者是创建型,前者是行动型
建筑者模式和工厂模式区别?
建造者模式是一步一步生成产品。抽象工厂是一下返回一个产品
1.3 建造者模式 难度系数 ** 应用系数***
又名生成器模式,是一种对象构建模式。将简单的模块,组成复杂的应用.
builder模式与一下子就生成产品的创建型模式不同,它是在导向者的控制下一步一步构造产品的。
关键字:builder
关键代码

public interface Builder {
    public void buildPart1();
    public void buildPart2();
    public Product retrieveResult();
}

1.4 原型模式 难度系数 ** 应用系数*
原型模式:就是clone,原来的对象copy一份.clone 有两种使用方法,方法1 .浅拷贝clone; 方法2:深拷贝.如果类有对象类型变量,需要深拷贝需要实现clone接口。
关键字: clone
关键代码:
浅拷贝:没有对象类型

Student student1 = new Student(1, "张三", scores);
Student student2 = student1.clone();

深拷贝: 含有对象map类型

protected Student clone()  {
    Student student = null;
    try {
        student = (Student) super.clone();
        student.scores = (Map<String, Double>) ((HashMap)this.scores).clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    return student;
}

原型模式和拷贝构造函数区别?
原型模式是clone(有浅拷贝和深拷贝两种实现方法)。拷贝构造函数是浅拷贝
2 结构型模式
2.1 适配器模式 难度系数 ** 应用系数*
真实世界场景两项插头转成三项插头.
通俗的说是外壳不换,换内容
关键字:adaptor
关键代码:接口1调用adaptee 2

public class Adapter {
 public void sampleOperation1(){
        this.adaptee2.sampleOperation1();
    }
}

这样做的好处是不用改成对外接口(api),外层可能有数百处Adapter.sampleOperation1 调用的地方都不用改.
第一个项目就是两项插头,第二个项目是三项插头。则第二个项目仅仅修改上面的sampleOperation1中的调用即可
2.2 装饰模式 难度系数 ** 应用系数*
装饰者:一个对象添加一些额外的职责
装饰者模式: 后面基于前面,加法关系不需要整体-部分关系. 比如:咖啡,加奶,加糖,加绿茶。
一层基于一层的添加,每层都是独立应用
关键代码:

DataOutputStream out = new DataOutputStream(
    new BufferedOutputStream(
      new FileOutputStream(
        new File(file))));

2.3 组合模式 难度系数 ** 应用系数*
几个团组合一个师,几个师组成一个军。
组合模式:树状结构

public class Client {
    public static void main(String[] args) {
        // 定义多个Composite组合对象
        Component root = new Composite("服装");
        Component c1 = new Composite("男装");
        Component c2 = new Composite("女装");
        // 定义多个Leaf叶子对象
        Component leaf1 = new Leaf("西服");
        Component leaf2 = new Leaf("夹克");
        Component leaf4 = new Leaf("裙子");
        Component leaf5 = new Leaf("套装");
        // 组合成为树形的对象结构
        root.addChild(c1);
        root.addChild(c2);
        c1.addChild(leaf1);
        c1.addChild(leaf2);
        c2.addChild(leaf4);
        c2.addChild(leaf5);
        // 调用根对象的输出功能输出整棵树
        root.someOperation("");
    }
}

2.4 外观模式(门面模式) 难度系数 ** 应用系数*
外观模式的名称叫facade,类似android的中台facade作用。
一键完成功能,即将几个class封装到一个class中.注意是class级别的封装,不同于函数级别的封装.
真实世界对比:小米在家中的一键观电影模式,自动关窗帘,调低亮灯,打开电视和音箱等
这个设计模式仅仅是再封装,有点滥竽充数的意思.
这个设计模式仅仅是再封装,有点滥竽充数的意思
2.5 享元模式 ** 难度系数 ** 应用系数**
享元模式的好处是共享,比如 围棋黑白子,如果每个棋子画一个可能需要200多个,但是套用黑白子则仅仅两个,只用加位置不同即可.它核心是用一个hash表key-value,有key直接取value,没有key先save在返回value
关键字: hash
关键代码

public class FlyweightFactory{
    private HashMap flyweights = new HashMap();
    public Flyweight getFlyweight(String key){
        if(flyweights.containsKey(key)){
            return (Flyweight)flyweights.get(key);
        }
        else{
            Flyweight fw = new ConcreteFlyweight();
            flyweights.put(key,fw);
            return fw;
        }
    }
}

享元模式和单例模式的区别?
单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
享元模式也是get value。但是它单例不同,1)单例只取一份,享元无数份 2) 单例是对象,享元是对象中的value
2.6 代理模式 难度系数 ** 应用系数**
两种代理:静态和动态
即不直接使用A类,保护了A的隐私性.而是在B中使用A.给外界的接口是B.A与B是一回事.B is 代理.类似封装不直接使用变量,而是使用get函数。
动态: java 反射
关键字:封装,动态 invoke
关键代码

public class ProxyObject extends AbstractObject{
    RealObject realObject = new RealObject();
    @Override
    public void operation() {       
        realObject.operation();        
    }
}

2.7 桥接模式 难度系数 ** 应用系数*
记定义这个问题很难理解,但是记得下面的例子,则非常容易理解.
impl 实现分离上层和下层, 下面是linux实现 ,windows实现不care.
关键字:impl.
下面是网上例子的分析.
https://www.cnblogs.com/java-my-life/archive/2012/05/07/2480938.html
就是两种类型的class分开,然后聚合nN.一种class是 种类比如sms,紧急sms;一种class是操作,实现,比如发送。
之前是用继承实现。这种扩展两种类都要扩展,即扩展接口及其子类。但是子类是类似的,所以需要统一.
不是按照功能单一类型class划分,而是按照功能和实现类两种类型class划分,然后n
N结合.
把多层继承,变成了单层继承。
https://www.jianshu.com/p/61ad71954c74
即impl模式;将抽象与实现解耦,使得二者可以独立变化
3. 行为型模式
3.1 策略模式 难度系数 *** 应用系数**
关键字:strategy
一个strategy对象封装一个算法.
两种class.第一个class是类型,种类.第二个class统一执行接口 (计算价格或者发送,不同策略(class1)不同的算法(class2))
类似适配器模式,第二个接口不变但是内容根据第一个class的设置而变化。
主要依赖第一个class。第一个class是第二个class的if 条件.
同时第一个class 是多态的,类似工厂模式。
策略模式=工厂模式+适配器模式
调用哪个对象即set 策略,然后运行这个策略的具体执行函数
ah.setSortObj(sort); //设置具体策略
result=ah.sort(arr); 执行策略
关键代码

Sort sort = new SelectionSort();    //使用选择排序
ah.setSortObj(sort); //设置具体策略
result=ah.sort(arr);

策略模式和状态模式?
两者类图一样.
状态/策略之间的关系:状态模式中的不同状态彼此相关,例如作为前一个或者后一个状态等。这是因为在状态之间像有限状态机有一个流动。
策略模式只是从多个可用策略中选择一个策略,策略之间没有后者/前者的关系。
3.2 模板方法模式 难度系数 * 应用系数***
继承模式,将各个类共同的函数,变成一个class。然后各种类继承这个抽象类即可。
关键字:继承
也是滥竽充数的模式
3.3 观察者模式 难度系数 ** 应用系数**
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
分成两种推模型和拉模型.
订阅-分发(广播)
关键字:observer,notify,subject
应用场景:c++ 常用的callback 是观察者模式.比如angularjs常用.ose 系统的sebs

public interface Subject {
    public void registerObserver(Observer observer);
    public void notifyObserver();
}

3.4 状态模式 难度系数 ** 应用系数**
• 一个state对象封装一个与状态相关的行为.
关键字:state
应用场景:状态机.
两个class,一个是操作类 一个是状态类。 两者不是并列关系,前者使用后者(依赖关系)。
核心是第二个类,一切依赖第二个class的情况,决定了第一个class的结果。第二个class是第一个的判断条件
将原来每种操作都要重复写一遍各个状态的code,将重复的code类(函数)中。
即每个操作函数,比如bookroom和checkroom的时候都调用这段重复的code即可,不用按照分支每个分支写一遍即重复code写了2遍.
3个状态类,这个三个状态分别对于这:空闲、预订、入住。其中空闲可以完成预订和入住两个动作,预订可以完成入住和退订两个动作,入住可以退房。

  public void bookRoom(){
        if(state == FREEMTIME_STATE){   //空闲可预订
            if(count > 0){
                System.out.println("空闲房间,完成预订...");
                state =  BOOKED_STATE;     //改变状态:已预订
                count --;           
                if(count == 0){//房间预订完了,提示客户没有房源了
                    System.out.println("不好意思,房间已经预订完,欢迎您下次光临...");
                }
            }
            else{
                System.out.println("不好意思,已经没有房间了....");
            }
        }
        else if(state == BOOKED_STATE){
            System.out.println("该房间已经被预订了...");
        }
        else if(state == CHECKIN_STATE){
            System.out.println("该房间已经有人入住了...");
        }
    }

3.5 备忘录模式 难度系数 * 应用系数*
关键字:save,restore
备忘录模式: 太简单了。就是创建一个new 对象,save。restore的时候调用save的新对象
滥竽充数的模式
3.6 迭代模式 难度系数 ** 应用系数**
关键字 iterator
应用场景:stl中的iterator
一个iterator对象封装访问和遍历一个聚集对象中的各个构件的方法.
和其他模式不同,这个不是优化(封装等)类的设计。这个模式是优化类的调用。
old方式调用一个class 需要while 遍历一轮。现在将所有调用的while 变成统一(iterator遍历(与子类通用的指针)).
传入不同的class即可,即可全都调用
代码1:

public class FilmMenuIterator implements Iterator{
    MenuItem[] menuItems;
    int position = 0;    
    public FilmMenuIterator(MenuItem[] menuItems){
        this.menuItems = menuItems;
    }   
public boolean hasNext() {
    if(position > menuItems.length-1 || menuItems[position] == null){
            return false;
        }
        return true;
    }
    public Object next() {
        MenuItem menuItem = menuItems[position];
        position ++;
        return menuItem;
    }
}

代码2:

 while(iterator.hasNext()){
        MenuItem menuItem = (MenuItem) iterator.next();
   }

3.7 命令模式 难度系数 * 应用系数***
正常使用一个class的各个函数,没想到这也是设计模式。滥竽充数的一个模式.
应用场景:撤销命令,menu菜单
3.8 职责链模式 难度系数 ** 应用系数*
击鼓传花的顺序.每层处理处理方法一样,每次根据权限处理一些scenario
关键字:递归。
情况1:一层调用一层,每层处理一种case,如果处理不了交给下一层处理
但是觉得这种情况完成可以一层,不同的if-case处理,没有必要一层一层传递。
情况2:虽然各层函数名称一样,但是内容不一样,第一层if一种情况,对入参处理,传给下一层的时候传的参数变了。所以第2层是依赖第一层的不同case的结果的。
也是每层处理一种情况。但是不是并列关系,并列关系即情况1. link 的 源码例子是config不同,传给下一层。这个有用

    public void handleRequest(LeaveNode LeaveNode) {
        if(LeaveNode.getNumber() <= 3){   //小于3天辅导员审批
            System.out.println("辅导员" + name + "审批" +LeaveNode.getPerson() + "同学的请假条,请假天数为" + LeaveNode.getNumber() + "天。");
        }
        else{     //否则传递给系主任
            if(this.successor != null){
                this.successor.handleRequest(LeaveNode);
            }
        }
    }

3.9 中介者模式 难度系数 ** 应用系数* *
关键字: mediator
一个Mediator对象封装对象间的协议.class多对多类关系。
定义:如果一个系统中对象之间的联系呈现为网状结构,对象之间存在大量多对多的关系,将熬制关系及其复杂,这些对象称之为 “同事对象”.我们可以设置一个中介者对象,使各个同事对象只跟中介者打交道,将复杂的网络结构化解为星形结构.
现实模型,n个房东对n个client,每个client 要求不同的房子,直接找中介,中介有所有房东的接口。
这样,client就找中介即可
client和房东没关系
关键代码:

         //一个房主、一个租房者、一个中介机构
        MediatorStructure mediator = new MediatorStructure(); 
        //房主和租房者只需要知道中介机构即可
        HouseOwner houseOwner = new HouseOwner("张三", mediator);
        Tenant tenant = new Tenant("李四", mediator);     
        //中介结构要知道房主和租房者
        mediator.setHouseOwner(houseOwner);
        mediator.setTenant(tenant);

3.10 解释器模式 难度系数 ** 应用系数*
关键字:interpret
应用场景太小就适合语法解释器
两个关键字:非终结计算就是按照规则拆分,终结则是计算
关键代码:

终结点:
  public boolean interpret(Context ctx) {   
        return left.interpret(ctx) && right.interpret(ctx);
    }
非终结点
  public boolean interpret(Context ctx) {    
        return value;
    }

3.11 访问者模式 难度系数 *** 应用系数*
问题引出,每个visitor都要重复的一份element,避免这种情况,只留一份element,用访问者模式.
访问者模式=动态双分派
关键字:accept ;动态双分派. visitor,element,obstruct 3要素.
动态单分派即多态,静态多分派即重载。没法动态双分派。
所以采用了两次多态。第一次accept,第二次accept 调用visitor .
第一次element以visitor作为参数,转入element class accept.第二次visitor以element为参数,转入visitor class 具体执行
感觉第二次调用visitor是策略模式,等于策略模式外层加了一层accept?
元素说明:
Visitor就像我们去商场购物结账时候的收银员,具体的访问者如收银员甲,收银员乙;
Element如同我们所要购买的商品货物,具体的Element就是我们的商品A,商品B,
objectStructure就像一个购物车一样.
动态双分派:
在java中支持动态动态单分派和静态多分派,不支持动态多分派。
静态分派的参数类型是编程时已经确定的,不支持在运行时刻动态确定其参数类型,所以一定程度上不满足多态的要求,
如果想要java支持动态多分派,就必须执行两次动态的单分派,这种编程方式称为双重分派.
与双分派(多分派的一种特殊情况)相对的,是单分派的概念,在选择方法(函数)时,只有一个参量(影响因素)决定具体是哪个方法.
比如说多态,虚函数的调用只有在运行时根据对象的具体类型才能决定调用的是哪个函数,多态属于动态单分派,
含有多个参数的函数重载,则属于多分派,每个参数都是一个影响因素。即多分派就是影响方法选择的因素有多个,特别的,影响方法选择的因素有两个则称为双分派。
访问者模式是一种双分派的体现,即由Element的具体子类型和Visist的具体子类型决定了Accept操作的不同功能。
https://www.cnblogs.com/chenssy/p/3339756.html
写的最好的访问者模式. 例子精妙.看code理解原理。 没有生动的例子仅仅code理解起来很费事,仅仅有例子没有code理解不了具体如何实现
访问者模式是3个class的结合。
访问者模式3种类(3要素),比其他多1个class。
home=obstruct,小猫小狗=element,主人,小学生,附近的农民,附近的科学家=visit
关键代码

public void accept(Visitor visitor) {
        visitor.visitor(this);
}
public class Charger extends Visitor{
    public void visitor(MedicineA a) {
        System.out.println("划价员:" + name +"给药" + a.getName() +"划价:" + a.getPrice());
    }
    public void visitor(MedicineB b) {
        System.out.println("划价员:" + name +"给药" + b.getName() +"划价:" + b.getPrice());
    }   
}

https://segmentfault.com/a/1190000012495957
属于难度比较大,应用很少的比较鸡肋的模式.
4 设计模式原则:
(1) 单一原则,一个class实现一个功能
(2) 开闭原则: 抽象成接口,实现在具体类中
(3)多态, 里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象.在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。
(4) 依赖注入:构造注入,设值注入(Setter注入)和接口注入
(5) 接口隔离原则:接口细分
(6) 合成复用原则:复用时要尽量使用组合/聚合关系(关联关系),少用继承
(7) 迪米特法则:使类与类之间保持松散的耦合关系;比如使用中介模式
参考:
https://www.cnblogs.com/chenssy/category/482229.html
https://www.cnblogs.com/java-my-life/default.html?page=3

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章