1.设计模式的三大分类
设计模式分为三大类:
- 创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程。
- 结构型模式:通过对多个类和对象进行组合得到复杂结构的类,一般使用继承或者成员变量引用形式来实现。
- 行为型模式:行为模式不仅表达了对象和类,还表达了他们之间的交互,涉及到了对象和算法的分配。
每种设计模式类型包含的设计模式:
- 创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
- 结构型模式(7种):适配器模式,装饰者模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
- 行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
2.设计模式的六大原则
总原则:开闭原则
即对扩展开放,对修改封闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。
1.单一职责原则
一个类只负责一项职责,尽量做到类的只有一个行为原因引起变化。
单一职责原则告诉我们:一个类不能太“累”!在软件系统中,一个类(大到模块,小到方法)承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封装在同一类中。
2、里氏替换原则(Liskov Substitution Principle)
子类可以扩展父类的功能,但不能改变原有父类的功能。
里氏替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。即可以使用基类的地方都可以使用子类替代。
里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立。
3、依赖倒转原则(Dependence Inversion Principle)
面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。
4、接口隔离原则(Interface Segregation Principle)
每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。
5、迪米特法则(最少知道原则)(Demeter Principle)
一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。
最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。
6、合成复用原则(Composite Reuse Principle)
尽量首先使用合成/聚合的方式,而不是使用继承。即首先考虑作为成员变量来调用另一个类的方法。
3.java常用设计模式
以下为创建型模式==================================================================================
3.1 单例模式
在内部创建一个实例,构造器全部设置为private,所有方法均在该实例上改动,在创建上要注意类的实例化只能执行一次,可以采用许多种方法来实现,如Synchronized关键字,或者利用内部类等机制来实现。
单例模式又分为懒汉模式与饿汉模式。
饿汉模式:
public class Singleton {
private static Singleton value = new Singleton();
private Singleton(){}
public Singleton getInstance(){
return value ;
}
}
懒汉模式:
public class Singleton {
private static class SingletonClassInstance{
private static final Singleton instance=new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return SingletonClassInstance.instance;
}
}
3.2 抽象工厂模式
一个基础接口定义了功能,每个实现接口的子类就是产品,然后定义一个工厂接口,实现了工厂接口的就是工厂,这时候,接口编程的优点就出现了,我们可以新增产品类(只需要实现产品接口),只需要同时新增一个工厂类,客户端就可以轻松调用新产品的代码。
抽象工厂的实现:
先定义一个抽象工厂,定义两个方法返回两种产品的类型
public interface AbstractFactory {
/**
* 创建产品A
* @return
*/
public AbstractProductA createProductA();
/**
* 创建产品B
* @return
*/
public AbstractProductB createProductB();
}
然后定义公共的产品接口
public interface AbstractProductA {
}
public interface AbstractProductB {
}
然后分别实现两种产品
public class ProductA1 implements AbstractProductA{
}
public class ProductA2 implements AbstractProductA{
}
public class ProductB1 implements AbstractProductB{
}
public class ProductB2 implements AbstractProductB{
}
最后实现抽象工厂,分别创建两种相关联的产品A1-B1,A2-B2的具体工厂
public class ConcreteFactory1 implements AbstractFactory{
@Override
public AbstractProductA createProductA() {
// TODO Auto-generated method stub
return new ProductA1();
}
@Override
public AbstractProductB createProductB() {
// TODO Auto-generated method stub
return new ProductB1();
}
}
public class ConcreteFactory2 implements AbstractFactory{
@Override
public AbstractProductA createProductA() {
// TODO Auto-generated method stub
return new ProductA2();
}
@Override
public AbstractProductB createProductB() {
// TODO Auto-generated method stub
return new ProductB2();
}
}
客户端调用:
public class Client {
public static void main(String[] args) {
//------------- 创建A1-B1
AbstractFactory f1 = new ConcreteFactory1();
f1.createProductA();
f1.createProductB();
//--------------创建A2-B2
AbstractFactory f2 = new ConcreteFactory1();
f2.createProductA();
f2.createProductB();
}
}
3.3 工厂方法模式
作为抽象工厂模式的孪生兄弟,工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,也就是说工厂方法模式让实例化推迟到子类。
工厂方法模式非常符合“开闭原则”,当需要增加一个新的产品时,我们只需要增加一个具体的产品类和与之对应的具体工厂即可,无须修改原有系统。同时在工厂方法模式中用户只需要知道生产产品的具体工厂即可,无须关系产品的创建过程,甚至连具体的产品类名称都不需要知道。虽然他很好的符合了“开闭原则”,但是由于每新增一个新产品时就需要增加两个类,这样势必会导致系统的复杂度增加。
1. 定义产品即可和工厂接口
/**
* 冰箱接口
*/
public interface IRefrigerator {
//获取品牌名
String getBrandName();
//获取价格
double getPrice();
}
/**
* 冰箱工厂接口
*/
public interface IRefrigeratorFactory {
IRefrigerator getIRefrigerator();
}
2. 产品实现类
/**
* 海尔冰箱
*/
public class HaiErRefrigerator implements IRefrigerator {
@Override
public String getBrandName() {
return "海尔冰箱";
}
@Override
public double getPrice() {
return 5999;
}
}
/**
* 美的冰箱
*/
public class MeiDiRefrigerator implements IRefrigerator {
@Override
public String getBrandName() {
return "美的冰箱";
}
@Override
public double getPrice() {
return 2999;
}
}
/**
* 格力冰箱
*/
public class GeLiRefrigerator implements IRefrigerator {
@Override
public String getBrandName() {
return "格力冰箱";
}
@Override
public double getPrice() {
return 3999;
}
}
3. 工厂实现类
/**
* 海尔冰箱工厂
*/
public class HaiErRefrigeratorFactory implements IRefrigeratorFactory {
@Override
public IRefrigerator getIRefrigerator() {
return new HaiErRefrigerator();
}
}
/**
* 美的冰箱工厂
*/
public class MeiDiRefrigeratorFactory implements IRefrigeratorFactory {
@Override
public IRefrigerator getIRefrigerator() {
return new MeiDiRefrigerator();
}
}
/**
* 格力冰箱工厂
*/
public class GeLiRefrigeratorFactory implements IRefrigeratorFactory {
@Override
public IRefrigerator getIRefrigerator() {
return new GeLiRefrigerator();
}
}
4. 测试类
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
IRefrigeratorFactory refrigeratorFactory = new HaiErRefrigeratorFactory();
IRefrigeratorFactory refrigeratorFactory2 = new GeLiRefrigeratorFactory();
IRefrigeratorFactory refrigeratorFactory3 = new MeiDiRefrigeratorFactory();
IRefrigerator refrigerator = refrigeratorFactory.getIRefrigerator();
System.out.println("您购买了:" + refrigerator.getBrandName() + ",您需要支付:" + refrigerator.getPrice());
}
}
3.4 建造者模式
对于建造者模式而已,它主要是将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。适用于那些产品对象的内部结构比较复杂。
建造者模式将复杂产品的构建过程封装分解在不同的方法中,使得创建过程非常清晰,能够让我们更加精确的控制复杂产品对象的创建过程,同时它隔离了复杂产品对象的创建和使用,使得相同的创建过程能够创建不同的产品。但是如果某个产品的内部结构过于复杂,将会导致整个系统变得非常庞大,不利于控制,同时若几个产品之间存在较大的差异,则不适用建造者模式,毕竟这个世界上存在相同点大的两个产品并不是很多,所以它的使用范围有限。
实现:https://www.jianshu.com/p/47329a94f5dc
以下为结构型模式==================================================================================
3.5 适配器模式
在我们的应用程序中我们可能需要将两个不同接口的类来进行通信,在不修改这两个的前提下我们可能会需要某个中间件来完成这个衔接的过程。这个中间件就是适配器。所谓适配器模式就是将一个类的接口,转换成客户期望的另一个接口。它可以让原本两个不兼容的接口能够无缝完成对接。
作为中间件的适配器将目标类和适配者解耦,增加了类的透明性和可复用性。
实现:https://www.cnblogs.com/eric-fang/p/9122564.html
3.6 装饰者模式
我们可以通过继承和组合的方式来给一个对象添加行为,虽然使用继承能够很好拥有父类的行为,但是它存在几个缺陷:一、对象之间的关系复杂的话,系统变得复杂不利于维护。二、容易产生“类爆炸”现象。三、是静态的。在这里我们可以通过使用装饰者模式来解决这个问题。
装饰者模式,动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。虽然装饰者模式能够动态将责任附加到对象上,但是他会产生许多的细小对象,增加了系统的复杂度。
实现:https://www.cnblogs.com/shenwen/p/10768332.html
3.7 代理模式
代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的引用。它使得客户不能直接与真正的目标对象通信。代理对象是目标对象的代表,其他需要与这个目标对象打交道的操作都是和这个代理对象在交涉。
代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了的作用和保护了目标对象的,同时也在一定程度上面减少了系统的耦合度。
实现:https://www.jianshu.com/p/5fdc1c5b0c0f
以下为行为型模式==================================================================================
3.8 策略模式
策略模式就是定义了算法族,分别封装起来,让他们之前可以互相转换,此模式然该算法的变化独立于使用算法的客户。
在策略模式中它将这些解决问题的方法定义成一个算法群,每一个方法都对应着一个具体的算法,这里的一个算法我就称之为一个策略。虽然策略模式定义了算法,但是它并不提供算法的选择,即什么算法对于什么问题最合适这是策略模式所不关心的,所以对于策略的选择还是要客户端来做。客户必须要清楚的知道每个算法之间的区别和在什么时候什么地方使用什么策略是最合适的,这样就增加客户端的负担。
同时策略模式也非常完美的符合了“开闭原则”,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。但是一个策略对应一个类将会是系统产生很多的策略类。
实现:https://www.cnblogs.com/MrRightZhao/p/8000421.html
3.9 观察者模式
何谓观察者模式?观察者模式定义了对象之间的一对多依赖关系,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并且自动更新。
在这里,发生改变的对象称之为观察目标,而被通知的对象称之为观察者。一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,所以么可以根据需要增加和删除观察者,使得系统更易于扩展。所以观察者提供了一种对象设计,让主题和观察者之间以松耦合的方式结合。
实现:https://www.jianshu.com/p/d5a758dd2795