1.设计模式分类
共有23种分为三大类:
创建者模式:创建者模式又叫建造者模式,是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
结构性模式:对象的结构,组成,以及对象之间的依赖关系。
行为模式:对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:
2.六大原则
2.1开闭原则
原则:可以拓展扩展原代码,不能修改原代码。
优点:扩展性好,易于维护升级。
实现:多用接口、抽象类。
2.2里氏替换原则
原则:任何基类可以出现的地方,子类一定可以出现。
优点:对实现抽象化的具体步骤的规范。
实现:子类对父类的方法尽量不要重写和重载。
2.3依赖倒转原则
原则:开闭原则的基础,面向接口编程,依赖于抽象而不依赖于具体。
实现:用到具体类时,不与具体类交互,而与具体类的上层接口交互。
2.4接口隔离原则
原则:使用多个专门的接口比使用单一的总接口要好。
优点:避免接口污染(一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。)
实现:一个类对另外一个类的依赖性应当是建立在最小的接口上的。
2.5迪米特法则(最少知道原则)
原则:一个类对自己依赖的类知道的越少越好。
优点:被依赖的类变化时,才能最小的影响该类。
实现:无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。
2.6合成复用原则
原则:原则是尽量首先使用合成/聚合的方式,而不是使用继承。
优点:就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分。
实现:就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分。
3.创建型模式
所有设计模式都是用来解耦的
3.1工厂模式
提供了创建对象的一种最佳方式。
优点:创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同
的接口来指向新创建的对象,用来解耦。
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类工厂模式使其创建过程延迟到子类进行。
使用场景:日志记录器、数据库访问。
注意事项:复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
创建一个接口:
实现代码设计模式PPT-P10
3.2抽象工厂
围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂它提供了一种创建对象的最佳方式。
优点:接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象,用来解耦。
使用场景:QQ换皮肤,一起换;生成不同的操作系统程序。
注意事项:产品族扩展,产品等级易拓展。
实现代码设计模式PPT-P13
3.3单例模式
涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建
优点:这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象,用来减少垃圾对象和缓存用。
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
使用场景:要求生产唯一序列号 、WEB 中的计数器、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
注意事项:1.单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
4、getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
3.3.1懒汉式,线程安全
这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3.3.2饿汉式—线程安全
这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){
}
public static Singleton getInstance() {
return instance;
}
}
3.3.3双检锁/双重校验锁—线程安全
这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
3.3.4登记式/静态内部类 —线程安全
优点:这种方式能达到双检锁方式一样的功效,但实现更简单。
使用场景:对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
它利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
3.3.5枚举 —线程安全
描述:这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。这种实现方式还没有被广泛采用
优点:它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
3.4适配器模式
两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能
优点:将目标类和适配者类解耦、增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性、灵活性和扩展性都非常好,符合开闭原则
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
实现代码设计模式PPT-P31
3.5代理模式
创建具有现有对象的对象,以便向外界提供功能接口。,一个类代表另一个类的功能。
优点:
职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
(2).代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
(3).高扩展性
组成:
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
意图:为其他对象提供一种代理以控制对这个对象的访问。
使用场景:1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。
主义事项:
1.和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
2.和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
实现代码设计模式PPT-P36
3.6策略模式
一个类的行为或其算法可以在运行时更改。创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
主要解决:在有多种算法相似的情况下,
优点:
1、算法可以自由切换。
2、避免使用多重条件判断。
3、扩展性良好。
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
使用场景:
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
实现代码设计模式PPT-P48
3.7责任链模式
每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
优点:实现了请求者与处理者代码分离:发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。提高系统的灵活性和可扩展行。
意图:意图:
避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
使用场景:
1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
3、可动态指定一组对象处理请求。
注意事项:在 JAVA WEB 中遇到很多应用。