设计模式七大原则

单一职责原则

对于一个类应该只负责一项职责。

接口隔离

客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小接口上。

依赖倒转

  • 高层模块不应该依赖底层模块,两者都应该依赖其抽象
  • 抽象不应该依赖细节,细节应该依赖抽象
  • 依赖倒转的核心是面向接口编程
  • 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定得多。以抽象为基础搭建的架构比以细节为基础的架构要稳定得多。在java中,抽象指的是接口或抽象类,细节上实现类
  • 使用接口或者抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给实现类
package top.yawentan.depandencyInverse;interface Massage{String getMassage();
}class Person{//依赖倒转,高层模块依赖了底层模块,这违反了依赖倒转原则public void getMassageError(Email email) {System.out.println(email.getMassage());}//对接口的依赖,满足依赖倒转public void getMassage(Massage massage) {System.out.println(massage.getMassage());}
}class Email implements Massage{@Overridepublic String getMassage() {// TODO Auto-generated method stubreturn "收到邮件信息为";}}
class WeChat implements Massage{@Overridepublic String getMassage() {// TODO Auto-generated method stubreturn "收到微信信息为";}}public class Depandency {public static void main(String[] args) {Person p = new Person();p.getMassage(new Email());p.getMassage(new WeChat());}
}

里氏替换

OOP中存在的问题:继承在给程序设计带来便利的同时,也带来了弊端。继承会给程序带来侵入性,程序的可以移植性变差,增加对象之间的耦合。修改父类时相应的也修改了所有子类。

如何正确的使用继承===>里氏替换原则。

所有引用基类的地方必须能透明的使用其子类的对象
在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类方法
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合组合依赖来解决问题。

开闭原则ocp(最核心本质的原则)

  • 一个软件实体入类,模块和函数应该对扩展开发(对提供方),修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
  • 当软件需求有变化时,尽量通过扩展软件的实体行为来实现变化,而不是通过修改已有的代码来实现变化。

迪米特法则

  • 一个对象应该对其他对象保持最少了解,类和类关系越密切,耦合度越大

  • 迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不泄露任何信息。

    直接朋友:出现在成员变量,方法返回值,方法参数中的类为直接朋友。

合成复用原则

原则上尽量使用合成/聚合的方式,而不是使用继承,继承关系的耦合性最强

聚合(Aggregation):通过成员变量和set方法赋值
组合(Composite):通过成员变量和构造方法赋值
依赖(Dependency):通过形参列表传入

设计原则的核心思想

  • 找出应用中可能需要变化之处,把它们独立出来,不要和哪些不需要变化的代码混在一起。
  • 针对接口编程,而不是针对实现编程。
  • 为了交互对象之间的松耦合设计而努力

UML(Unified modeling language UML)统一建模语言

依赖(Dependency):只要在类中用到了对方,那么他们之间就存在依赖关系。
泛化(Generalization):继承
实现(Realization):实现接口
关联(Associate):类与类之间的关系,他是依赖关系的特例
聚合(Aggregation):整体与部分之间的关系,可分开。
组合(Composite):通过成员变量和构造方法赋值

设计模式类型

  1. 创建者模式单例模式,抽象工厂模式,原型模式,建造者模式,工厂模式
  2. 结构型模式:适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
  3. 行为型模式:模板方法模式,命令模式,访问者模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式(Interpreter模式),状态模式,策略模式,职责链模式(责任链模式)

单例模式

  1. 饿汉式(静态常量)

    1. 私有化构造器
    2. 类内部创建对象
    3. 对外暴露一个静态的公共方法
    class Single{private Single() {};private final static Single signle = new Single();public static Single getInstance() {return signle;}
    }
    
  2. 饿汉式(静态代码块)

    class Single2{private Single2() {};private static Single2 signle;static {signle = new Single2();}public static Single2 getInstance() {return signle;}
    }
    

    饿汉式优缺点:

    优点:写法简单,在类加载的时候就完成实例化。避免了线程同步问题
    缺点:在类装载的时候就完成实例化,没达到懒加载的效果。如何从未使用这个实例,就会造成内存的浪费

  3. 懒汉式(线程不安全)

    在使用getInstance()方法时才新建,但该方法是线程不安全的。

    class Single3{private Single3() {};private static Single3 signle;public static Single3 getInstance() {if(signle==null)signle = new Single3();return signle;}
    }
    
  4. 懒汉式(线程安全,同步方法)

    在getInstance中加入synchronized,效率太低了,每次getInstance都要同步,但实际上只需要一次。

    class Single3{private Single3() {};private static Single3 signle;public synchronized static Single3 getInstance() {if(signle==null)signle = new Single3();return signle;}
    }
    
  5. 懒汉式(线程不安全,同步代码块)

    class Single4{private Single4() {};private static Single4 signle;public static Single4 getInstance() {Object obj = new Object();if(signle==null)synchronized(obj) {signle = new Single4();            }return signle;}
    }
    
  6. 双重检查(线程安全,推荐使用)

    线程安全,效率高,实现了懒加载。

    class Single4{private Single4() {};private static volatile Single4 signle;public static Single4 getInstance() {Object obj = new Object();if(signle==null)synchronized(obj) {if(signle==null)signle = new Single4();           }return signle;}
    }
    
  7. 静态内部类(推荐使用)

    静态内部类当外部类被装载时并不会被装载,只有在调用静态内部类的时候会被装载。并且,类加载时没有线程安全的问题。

    class Single5{private Single5() {};private static class SingletonInstance{private static final Single5 INSTANCE = new Single5();}public static Single5 getInstance() {return SingletonInstance.INSTANCE;}
    }
    
  8. 枚举

    enum Singleton{INSTANCE;public void method() {System.out.println("ok");}
    }
    
  9. Runtime使用的就是饿汉式单例模式,因为必然会用到,所以不会造成资源浪费。

简单工厂模式

模拟现实生活中的情况,用户只与商家交互,商家与工厂交互,工厂负责生产原材料。这种方式可扩展性强,修改少,符合OCP

工厂方法模式

定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象实例化推迟到子类。图中OrderPizza为工厂类

抽象工厂模式

将工厂抽象成两层,抽象工厂和具体实现工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂变成了工厂簇,更利于代码的维护和扩展。

原型模式

  1. 用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
  2. 原型模式是一种创建设计模式,允许一个对象再创建另一个可定制的对象,无需知道如何创建的细节
  3. 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝他们自己来实例创建对象,即object.clone()

关于浅拷贝和深拷贝

浅拷贝是值传递,对于引用数据类型,拷贝的是其地址,也就是说,多个拷贝对象都是指向同一个地址。
深拷贝是复制对象的所以成员变量,不同于浅拷贝,深拷贝对于引用数据类型是会开辟新的内存空间。
可以通过重写clone方法或者对象序列化。

原型模式的优缺点

优点:简化对象创建过程,提高效率
缺点:需要为每一个类配备一个clone方法,所以当需要对现有的类进行改造时,需要修改其源代码,违背了ocp原则。

建造者模式

将产品和产品建造过程解耦=>建造者模式

  • 建造者模式主要有4个关键元素,产品,抽象建造类,具体建造类,指挥者。来完成解耦。
  • 用户使用不同的具体建造者即可得到不同的产品
  • 增加新的具体建造者无需修改原有代码,符合OCP
  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式

抽象工厂模式与建造者模式的对比

采用抽象工厂模式不需要关系构建过程,只关心什么产品由什么工厂生产即可。
建造者模式则要求指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。

适配器模式

类适配器
将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容。
从用户的角度看不到被适配者,是解耦的。
用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口。
缺点:需要继承,增加了耦合,不满足合成复用原则。

对象适配器

接口适配器模式

当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每一个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择的覆盖父类中的某些方法来实现需求,适用于一个接口不想使用其所有的方法的情况。

SpringMVC通过适配器设计模式获取到对应的Controller

适配器设计模式看起来太舒服了!
这种设计模式简直是艺术!!!

桥接模式

桥接模式是指:将实现抽象放在两个不同的类层次中,使两个层次可以独立改变。
Bridge模式基于类的最小设计原则,通过使用封装,聚合及继承等行为让不同的类承担不同的职责。他的主要特点是把抽象与行为实现分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展。

装饰者设计模式

装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,他比继承更加有弹性,装饰者模式也体现了OCP

点咖啡加调料的例子,这个例子我的第一想法是桥接模式,但是对与有多个decorator时需要持有Listdecorator。

而装饰者模式采用递归的方法不断包装,装饰着模式和桥接模式的区别就是decorator和Dring是不是继承关系。

区别:桥接中的行为是横向的行为,行为彼此之间无关联;而装饰者模式中的行为具有可叠加性,其表现出来的结果是一个整体,一个各个行为组合后的一个结果。

组合模式(HashMap里面有组合模式影子)

部分整体模式,他创建了对象组的树形结构,将对象组合成树状结构以表示层次关系。
本质就是构建树。College和university中放入的依赖依然是OrganizationComponent接口,符合面向接口的编程。

外观模式

定义一个高层接口,给子系统中的一组接口提供一个一致的界面,来访问子系统中的一群接口,也就是说通过定义一个一致的接口(界面类),用来屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节=>外观模式

外观模式就和往上再封装一个意思。

享元模式(Flyweight Pattern)

运用共享技术有效地支持大量细颗粒度的对象,常用于系统底层开发,解决系统的性能问题。像数据库连接池里面都是创建好的连接对象,在这些连接对象中我们需要就可以直接拿来用,避免了重新创建,享元模式能够解决重复对象的内存浪费的问题。享元模式的经典应用场景就是池技术

  1. Flyweight是享元的抽象类

  2. ConcreteFlyWeight是具体的享元角色

  3. FlyweightFactory享元工厂类,用于构建一个池容器,同时提供从池内获取对象的方法

    JDK中Integer引入了缓存池技术IntegerCache[−128,127][-128,127][−128,127].

代理模式(Proxy)

为一个对象提供了一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。这样做的好处是可以在目标对象实现的基础上增强额外的功能操作,即扩展目标对象的功能。代理可以分为静态代理,动态代理(jdk,cglib)

静态代理主要通过目标类与代理类实现同一个接口。让代理类持有真实对象,然后在代理类的方法里面调用。在调用的前后写入我们的代码。这样就将代码写死了。

jdk动态代理代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理,代理对象的生成,是利用JDK的API动态的在内存中构建代理对象。

//动态代理类
package top.yawentan.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class ProxyFactory {private Object target;public ProxyFactory(Object obj) {super();this.target = obj;}//通过jdk获得库函数public Object getProxyInstance() {return Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler(){@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("jdk开始代理");return method.invoke(target, args);}});}
}

Cglib动态代理静态代理和JDK代理都需要目标对象实现一个接口,而Cglib是通过继承来实现代理的。Cglib底层是通过使用字节码处理框架ASM来转换字节码并生成新的类,注意代理类不能是final类。

模板方法模式(Template Method Pattern)

在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行

  • AbstractClass抽象类,类中实现了模板方法template,定义了算法的骨架,具体子类需要去实现,其他的抽象方法operation2,3,4。
  • ConcreteClass 实现抽象方法opetation2,3,4,以完成算法中特点子类的步骤。

模板方法的钩子方法

  • 在模板模式的父类中,我们定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为钩子。

命令模式(Command Pattern)

  • 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需要在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计。
  • 命令模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。

访问者模式

  • 封装一些作用于某种数据结构的各元素操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
  • 主要将数据结构与数据操作分离,解决数据结构操作耦合性问题
  • 访问者模式的基本原理是:在被访问的类里面加一个对外提供接待访问者的接口
  • 访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作,同时需要避免让这些操作污染这些对象的类,可以选用访问者模式解决

优点:访问者模式符合单一职责原则,OCP。访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用与数据相对稳定的系统。

缺点:具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,也就是不满足最小知道原则。违背了依赖倒转原则。访问者依赖的是具体的元素,而不是抽象的元素。

因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式是比较合适的。

迭代器模式(Iterator Pattern)

提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即不暴露其内部结构。

优点:提供了一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器而不会知道聚合的具体组成。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响迭代器。

缺点:每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类

观察者模式

对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为ObserverSubject通知Observer变化。

优点:观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。这样,我们增加观察者,就不需要去修改核心类WeatherData不会修改代码,遵守了OCP原则。

中介者模式

用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式的互相引用,从而使其耦合松散,而且可以独立的改变他们之间的交互

  • 多个类互相耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构,进行解耦
  • 减少了类间的依赖,降低了耦合,符合迪米特法则
  • 中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会收到影响
  • 如果设计不当,中介者对象本身变得过于复杂。

备忘录模式

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将对象恢复到原先保持状态。

  • 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便的回到某个历史的状态
  • 实现了信息的封装,使得用户不需要关心状态的保存细节
  • 如果类中成员变量过多,势必会占用较大的资源,而且每一次保存都会消耗一定的内存。
  • 为了节约内存,备忘录模式可以和原型模式配合使用

解释器模式(Interpreter Pattern)

给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)

状态模式(State Pattern)

主要用来解决在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以互相转换。当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。

  • 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
  • 符合开闭原则。容易增删状态

策略模式(Strategy Pattern)

  1. 定义算法族,分别封装起来,让他们之间互相替换,此模式让算法的变化独立于使用算法的客户
  2. 该算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体的类(定义了策略接口);第三多用组合/聚合,少用继承。

感觉就像再加一层缓冲层,而不是全写在一个接口里面,和桥接模式很像,都是通过组合的方式来连接策略和具体的对象。

职责链模式

为请求者创建了一个接收者对象的链(环形链表)。这种模式对请求的发送者和接收者进行解耦。(双亲委派机制)

  • 将请求和处理分开,实现解耦,提高系统的灵活性
  • 简化了对象,使对象不需要知道链的结构
  • 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量
  • 调式不方便。采用类似递归的方法,调式时逻辑可能比较复杂
  • 最佳应用场景:有多个对象可以处理同一个请求,比如:多级请求,请假/加薪等审批流程

设计模式学习笔记总结相关推荐

  1. 7 种 Javascript 常用设计模式学习笔记

    7 种 Javascript 常用设计模式学习笔记 由于 JS 或者前端的场景限制,并不是 23 种设计模式都常用. 有的是没有使用场景,有的模式使用场景非常少,所以只是列举 7 个常见的模式 本文的 ...

  2. java/android 设计模式学习笔记(1)--- 单例模式

    前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使 ...

  3. 设计模式学习笔记清单

    设计模式学习笔记清单 关于设计模式许多人已经耳熟能详,这段时间结合李建忠的教学视频以及大量网络资料,把这部分过了一遍,整理出学习笔记,而真正的深入学习和理解只能在具体的开发环境中日积月累.      ...

  4. 步步为营 .NET 设计模式学习笔记系列总结

    设计模式我从开篇到23种设计模式的讲解总共花了进两个月的时间,其间有很多读者给我提出了很好的建议,同时也指出了我的不足,对此我表示感谢,正是由于很多读者的支持我才能坚持的写到最后.在此表示我真诚的谢意 ...

  5. 设计模式学习笔记--Strategy 策略模式

    所谓策略模式(Strategy Pattern),就是将策略 (算法) 封装为一个对象,易于相互替换,如同 USB 设备一样可即插即用:如果将策略.具体的算法和行为,编码在某个类或客户程序内部,将导至 ...

  6. 设计模式学习笔记--Mediator 中介者模式

    我们知道面向对象应用程序是由一组为了提供某种服务而彼此交互的对象组成.当彼此引用的对象数量比较少时,此时对象之间就为直接交互(点对点).而当对象的数量增加时,这种直接交互会导致对象之间复杂的.混乱的引 ...

  7. 设计模式学习笔记(十七)——Command命令模式

    设计模式学习笔记(十七)--Command命令模式 Command命令模式介绍: Command命令模式是一种对象行为型模式,它主要解决的问题是:在软件构建过程中,"行为请求者"与 ...

  8. 设计模式学习笔记——解释器(Interpreter)模式

    设计模式学习笔记--解释器(Interpreter)模式 @(设计模式)[设计模式, 解释器模式, Interpreter] 设计模式学习笔记解释器Interpreter模式 基本介绍 解释器案例 类 ...

  9. 设计模式学习笔记——命令(Command)模式

    设计模式学习笔记--命令(Command)模式 @(设计模式)[设计模式, 命令模式, command] 设计模式学习笔记命令Command模式 基本介绍 命令案例 类图 实现代码 Command接口 ...

  10. 设计模式学习笔记——代理(Proxy)模式

    设计模式学习笔记--代理(Proxy)模式 @(设计模式)[设计模式, 代理模式, proxy] 设计模式学习笔记代理Proxy模式 基本介绍 代理案例 类图 实现代码 Printable接口 Pri ...

最新文章

  1. CxImage图像处理类库
  2. 刘铁岩:AI打通关键环节,加快物流行业数字化转型
  3. linue 查询端口号 netstat
  4. 【练习】ViewPager标签滑动
  5. asp.net 中GridView控件实现全选及反选的功能
  6. cocos2d-x坐标系
  7. java父系调用子系,获取usb设备父系或子代
  8. java string转decimal_java中string转bigdecimal的例子
  9. 修复Net4.0在IE11下doPostBack无效的问题
  10. Intellij idea 出现错误 error:java: 无效的源发行版: 11解决方法
  11. Mac下VirtualBox虚拟机Win7与主机共享文件夹
  12. 前端工程师需要懂的前端面试题(c s s方面)总结(二)
  13. Java中Spring报错org.springframework.core.annotation.AnnotationUtils.clearCache()V
  14. 经典实战教程!java编译器eclipse
  15. 《zabbix中文支持》-4
  16. 【日常小结】VB.NET下操作Access数据库
  17. SVN(三)利用 IntelliJ IDEA 进行代码对比的方法
  18. excel 度分秒转度
  19. 内存泄漏的原因及解决方法
  20. 在阿里我是如何当面试官的

热门文章

  1. Powershell 操作Excel的基本命令
  2. 广州大学学生实验报告,数据结构实验,二叉树的操作与实现
  3. 用计算机来解锁密码,电脑忘记开机密码怎么办?如何解锁?
  4. 通过Python爬取QQ空间说说并通过Pyechart进行可视化分析
  5. I2C、Arduino、ADXL345、
  6. 数据库连接技术 - 数据库连接池
  7. Cadence OrCAD Capture 检索和定位功能的介绍图文视频教程
  8. android平台下OpenGL ES 3.0绘制圆点、直线和三角形
  9. Centos安装traceroute
  10. window10无线可以上网却显示“无internet 安全” 状态栏显示未连接的地球图标