一 设计模式-类型
1 创建型模式
Creational Pattern
对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件结构更加清新,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使真个新系统的设计更加符合单一职责原则。
创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。
工厂方法模式 抽象工厂模式 建造者模式 单例模式 原型模式
2 结构型模式
适配器模式 装饰者模式 代理模式 外观模式 桥接模式 组合模式 享元模式
3 行为型模式
策略模式 观察者模式 责任链模式 备忘录模式 模板方法模式 迭代器模式 中介者模式 命令模式 访问者模式 解释器模式 状态模式
二 设计模式-原则
面向对象(封装,继承,多态,抽象,接口的定义和实现)
1 开闭原则
所有原则的基础。对扩展开放,对修改关闭。
2 单一职责原则
Single Responsibility Principle
定义:不要存在多于一个导致类变更的原因
优点:降低类的复杂度,提高类的可读性,提高系统的可维护性,降低变更引起的风险。
3 迪米特法则(最少知道原则)
Law of Demter / The Least Knowledge Principle
定义:一个对象应该对其他对象保持最少的了解。又叫最少知道原则。
优点:降低类之间的耦合度
一个类只与他的朋友类进行交互
直接的朋友:每个对象都会和其他对象有耦合关系,只要两个对象有耦合关系,我么就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接朋友,而出现在局部变量中的类不是直接朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
4 里氏替换原则
Liskov Substitution Principle
定义:一个软件实体如果适用一个父类的话,那一定适用于子类,所有引用父类的地方必须能透明地适用子类的对象,子类对象能替换父类对象,而程序逻辑不变。
引申定义:子类可以扩展父类的功能,但不能改变父类原有的功能。
含义1:子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
含义2:子类可以增加自己特有的方法。
优点1:约束继承泛滥,开闭原则的一种体现。
优点2:加强程序的健壮性,同时变更时也可以做到非常好的兼容性 提高程序的维护性、扩展性。降低需求变更时引入风险。
5 依赖倒置原则
Dependence Inversion Principle
定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象
抽象不应该依赖细节;细节应该依赖抽象
针对接口编程,不要针对实现编程
优点:可以减少类之间的耦合度、提高系统的稳定性,提高代码的可读性和可维护性,可降低修改程序所造成的风险。
6 接口隔离原则
Interface Segregation Principle
定义:用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口。
一个类对一个类的依赖应该建立在最小接口上
建议单一接口,不要建立庞大臃肿的接口
尽量细化接口,接口中的方法尽量少
注意适度原则,一定要适度
优点:符合我们场所的高内聚低耦合的设计思想,从而使得类具有很好的可读性、可扩展性和可维护性。
7 合成(组合)/聚合复用原则
定义:尽量使用对象的组合/聚合,而不是集成关系达到软件复用的目的。
优点:可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少。
三 设计模式
简单工厂模式
Simple Factory
工厂方法模式
Factory Method
抽象工厂模式
Abstract Factory
使用目的:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
优点:
一个调用者想创建一个对象,只需要知道其名称就可以了。
扩展性高,如果想增加一个产品,只需要扩张一个工厂类就可以。
屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:
每增加一个产品时,都要增加一个具体类和对应的实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同事也增加系统具体类的依赖。
使用场景:
1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器器等,用户可以选择记录到什么地方。
2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
3、设计一个连接服务器的框架,需要三个协议,POP3、IMAP、HTTP,可以这三个作为产品类,共同实现一个接口。
4、比如Hibernate换数据库只需换方言和驱动就可以。
身边的工厂模式:
JDK给开发者听了Executors这个类,可以让用户产生ThreadPoolExecutor和使用ThreadPoolExecutor分离开,比如可以让Executors提供一个单线程的线程池newSingleThreadExecutor newFixedThreadPool newCachedThreadPool newScheduledThreadPool,让开发者可以不用关心线程池是如何去实现的,直接使用Executors创建的ThreadPoolExecutor。
Connection connection = DriverManager.getConnection()
Logger log = LoggerFactory.getLogger
单例模式
构造函数私有
1、饿汉模式
HungrySingleton
2、懒汉模式
LazySingleton
3、双重检查懒汉模式
LazyDoubleCheckSingleton
volatile避免指令重排序
4、静态内部类
InnerClassSingleton
5、枚举
EnumSingleton
6、容器单例模式
ContainerSingleton
HashMap
7、ThreadLocal单例
反射攻击
序列化和反序列化
Object readResolve()
例子1 饿汉式
java.lang.Runtime
private static Runtime currentRuntime = new Runtime();
例子2 容器
java.awt.Desktop
例子3
package org.apache.ibatis.executor;
public class ErrorContext {
private static final ThreadLocal<ErrorContext> LOCAL = ThreadLocal.withInitial(ErrorContext::new);
private ErrorContext() {
}
public static ErrorContext instance() {
return LOCAL.get();
}
}
例子4
懒汉式单例。不需要事先创建好,只要在第一次真正用到的时候再创建就可以了。因为很多时候并不常用Java的GUI和其中的对象。如果使用饿汉单例的话会影响JVM的启动速度。
java.awt.Toolkit#getDefaultToolkit()
public abstract class Toolkit {
private static Toolkit toolkit;
public static synchronized Toolkit getDefaultToolkit() {
if (toolkit == null) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
Class<?> cls = null;
String nm = System.getProperty("awt.toolkit");
try {
cls = Class.forName(nm);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
try {
cls = cl.loadClass(nm);
} catch (final ClassNotFoundException ignored) {
throw new AWTError("Toolkit not found: " + nm);
}
}
}
try {
if (cls != null) {
toolkit = (Toolkit)cls.newInstance();
if (GraphicsEnvironment.isHeadless()) {
toolkit = new HeadlessToolkit(toolkit);
}
}
} catch (final InstantiationException ignored) {
throw new AWTError("Could not instantiate Toolkit: " + nm);
} catch (final IllegalAccessException ignored) {
throw new AWTError("Could not access Toolkit: " + nm);
}
return null;
}
});
loadAssistiveTechnologies();
}
return toolkit;
}
}
以上代码是Toolkit类的单例实现。这里类加载时只静态声明了私有toolkit并没有创建Toolkit实例对象,延迟加载加快了JVM启动速度。单例模式作为一种创建模式,在依赖加载的时候应用了另一种创建对象的方式,不是new新的对象,因为Toolkit本身是个抽象类不能实例化对象,而是通过反射机制加载类并创建新的实例。
建造者模式
原型模式
深拷贝和浅拷贝,深克隆和浅克隆。重要的是看拷贝的是不是地址。
外观模式
定义:又叫门面模式,提供一个统一的接口,来方位子系统中的一群接口
外观模式定义了一个高层接口,让子系统更容易使用
适用场景
子系统越来越复杂,增加外观模式听简单的调用接口
构建多层系统结构,利用外观对象作为每层的入口,简化层间调用
优点:
简化了调用过程,无需深入了解子系统,防止带来风险。
减少系统依赖,松散耦合
更好划分访问层次
符合迪米特法则,即最少知道原则
缺点:
增加子系统、扩展子系统行为容易引入风险
不符合开闭原则
适配器模式
两种实现方式,一种是继承,一种是组合
例子:org.springframework.web.servlet.DispatcherServlet
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
org.springframework.web.servlet.HandlerAdapter
HandlerFunctionAdapter
HttpRequestHandlerAdapter
RequestMappingHandlerAdapter
SimpleControllerHandlerAdapter
SimpleServletHandlerAdapter
装饰者模式
定义:在不改变原有对象的基础之上,将功能附加到对象上
提供了比继承更有弹性的替代方案(扩展原有对象功能)
适用场景
扩展一个类的功能或者给一个类添加附加职责
动态的给一个对象添加功能,这些功能可以再动态的撤销
优点:
继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能。
通过使用不同的装饰类以及这些装饰类的排列组合,可以实现不同效果
符合开闭原则
缺点:
会出现更多的代码,更多的类,增加程序复杂性
动态装饰时,多层装饰会更复杂
享元模式
组合模式
桥接设计模式
定义:将抽象部分与它的具体实现部分分离,使它们都可以独立变化
通过祝贺的方式建立两个类之间的联系,而不是继承
适用场景
抽象和具体实现之间增加更多的灵活性
一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展
不希望使用继承,或因为多层继承导致系统类的个数剧增
优点
分离抽象部分及其具体实现部分
提高了系统的可扩展性
符合开闭原则
符合合成复用原则
缺点:
增加了系统的理解和设计难度
需要正确识别出系统中两个独立变化的维度