目录
创建型模式
简单工厂模式(Simple Factory Pattern)
工厂方法模式(Factory Pattern)
抽象工厂模式(Abstract Factory Pattern)
单例模式(Singleton Pattern)
原型模式(Prototype Pattern)
建造者模式(Builder Pattern)
结构型模式
代理模式(Proxy Pattern)
适配器模式(Adapter Pattern)
桥接模式(Bridge Pattern)
装饰器模式(Decorator Pattern)
组合模式(Composite Pattern)
外观模式(Facade Pattern)
享元模式(Flyweight Pattern)
行为型模式
策略模式(Strategy Pattern)
观察者模式(Observer Pattern)
责任链模式(Chain of Responsibility Pattern)
模板方法模式(Template Pattern)
状态模式(State Pattern)
命令模式(Command Pattern)
中介者模式(Mediator Pattern)
迭代器模式(Iterator Pattern)
访问者模式(Visitor Pattern)
备忘录模式(Memento Pattern)
解释器模式(Interpreter Pattern)
设计模式(Design Patterns),指软件设计中被反复使用的一种代码设计经验,把一些常用的设计思想提炼出一个个模式,上个世纪90年代由ErichGamma、RichardHelm、RaplhJohnson和JonhnVlissides提出,被称为四人帮(GoF),GoF把23个常用模式分为创建型模式、结构型模式和行为型模式。其目的是为了提高程序代码的可扩展性、可维护性、可重用性。主要基于OOP编程提炼的,基于以下原则:
开闭原则(Open Closed Principle),由Bertrand Meyer提出,对扩展开放,对修改关闭。在增加新功能的时候,能不改代码尽量不要改,如果只增加代码就完成新功能,那是最好的。
里氏替换原则(Liskov Substitution principle),由Barbara Liskov提出,一种面向对象的设计原则,子类对象可以代替其基类对象,如果调用一个父类的方法可以成功,替换成子类调用也应该可以成功。
单一职责原则(Single Responsibility Principle,SRP),又称单一功能原则。单一职责原则的核心就是控制类的粒度大小,将对象解耦提高其内聚性,降低类的复杂度(一个类只负责一项职责,其逻辑要比负责多项职责简单得多),提高可读性和可维护性,比如修改一个功能可以降低对其他功能的影响)。
依赖倒置原则(Dependence Inversion Principle),是实现开闭原则的重要途径之一,在软件设计中细节具有多变性,而抽象层则相对稳定,以抽象为基础搭建起来的架构要比以细节为基础搭建起来的架构要稳定得多。这里抽象指的是接口或者抽象类,而细节是指具体的实现类。
接口隔离原则(Interface Segregation Principle,ISP),客户端不应该被迫依赖于它不使用的方法(Clients should not be forced to depend on methods they do not use)。该原则还有另外一个定义:一个类对另一个类的依赖应该建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)。要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想。两者不同点在于:单一职责原则注重的是职责,主要是约束类,它针对的是程序中的实现和细节。接口隔离原则注重的是对接口依赖的隔离,主要约束接口,针对抽象和程序整体框架的构建。
内聚性:着重于模块内部的复杂性,它衡量的是一个模块内各功能之间的关系强度。如果一个模块有明确的目的,并且只执行一项任务,这意味着该模块具有高内聚性。如果该模块试图执行一个以上的任务,并封装了一个以上的目的,那么该模块的内聚力就很低。
迪米特法则(Law of Demeter,LoD),又叫作最少知识原则(Least Knowledge Principle,LKP),1987年美国东北大学(Northeastern University)的一个名为迪米特(Demeter)的研究项目,由伊恩·荷兰(Ian Holland)提出,被UML创始者之一的布奇(Booch)普及,后来又因为在经典著作《程序员修炼之道》(The Pragmatic Programmer)提及而广为人知。其含义是如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
学习设计模式主要学习其设计思想,不能生搬硬套为了使用而使用,要合理平衡设计的复杂度和灵活性。总之啊,只要不是你觉到悟到的,那就不是你自己的,给了你也拿不住,自己学到感悟道的,实际应用中才有可能做到,能做到的才是自己的。
一个抽象产品:一个接口(定义产品要有哪些功能,让供应商来生产)。
两个供应商生产产品:两个实现类(一个供应商对应一个实现类)。
产品工厂:一个工厂类返回不同的实现类对象(供应商把自己的产品运输到指定工厂,我们直接从工厂拿产品)。示例如下:
public interface IOHandlerService {
void save(String key, String value);
String getString(String key);
}
//内存缓存实现
public class MemoryIOHandlerImpl implements IOHandlerService {
@Override
public void save(String key, String value) {
System.out.println(key + "" + value);
}
@Override
public String getString(String key) {
return null;
}
}
//磁盘缓存实现
public class DiskIOHandlerImpl implements IOHandlerService {
@Override
public void save(String key, String value) {
}
@Override
public String getString(String key) {
return null;
}
}
/**
* @Description工厂类
*/
public class IOHandlerFactory {
public enum IOType {
MEMORY, DISK, OTHER
}
public static IOHandlerService createIOHandler(IOType ioType) {
switch (ioType) {
case MEMORY:
return new MemoryIOHandlerImpl();
case DISK:
return new DiskIOHandlerImpl();
case OTHER:
return null;
default:
return null;
}
}
}
public static void main(String[] args) {
//通过IOHandlerFactory工厂类,传入不同的缓存名称,比如MEMORY、DISK,就可以切换对应的缓存方式
IOHandlerService iOHandlerService = IOHandlerFactory.createIOHandler(IOHandlerFactory.IOType.MEMORY);
iOHandlerService.save("name", "小雨");
iOHandlerService.save("age", "28");
String name = iOHandlerService.getString("name");
String age = iOHandlerService.getString("age");
System.out.println("name = " + name + " , age = " + age);
}
优点:解耦
缺点:如果新增一种缓存方式,需要改动原来的代码。
一个抽象产品:一个接口(定义产品要有哪些功能,让供应商来生产)。
三个供应商生产产品:三个实现类(一个供应商对应一个实现类)。
三个工厂:三个工厂类(供应商把自己的产品运输到自己的工厂,我们从不同供应商工厂拿产品)。示例如下:
//工厂类接口
public interface IOHandlerFactoryService {
IOHandlerService createIOHandler();
}
//内存缓存工厂类
public class MemoryIOHandlerFactory implements IOHandlerFactoryService {
@Override
public IOHandlerService createIOHandler() {
return new MemoryIOHandlerImpl();
}
}
//磁盘缓存工厂类
public class DiskIOHandlerFactory implements IOHandlerFactoryService {
@Override
public IOHandlerService createIOHandler() {
return new DiskIOHandlerImpl();
}
}
//反向代理缓存工厂类
public class ProxyIOHandlerFactory implements IOHandlerFactoryService {
@Override
public IOHandlerService createIOHandler() {
return new ProxyIOHandlerImpl();
}
}
public static void main(String[] args) {
//通过IOHandlerFactory工厂类,传入不同的缓存名称,比如MEMORY、DISK,就可以切换对应的缓存方式
// IOHandlerService iOHandlerService = IOHandlerFactory.createIOHandler(IOHandlerFactory.IOType.MEMORY);
// iOHandlerService.save("name", "小雨");
// iOHandlerService.save("age", "28");
// String name = iOHandlerService.getString("name");
// String age = iOHandlerService.getString("age");
// System.out.println("name = " + name + " , age = " + age);
//
IOHandlerFactoryService ioHandlerFactoryService = new MemoryIOHandlerFactory();
IOHandlerService handlerService = ioHandlerFactoryService.createIOHandler();
handlerService.save("test", 1);
}
优点:假如要新增一种缓存方式,直接新增两个类,一个工厂类一个实现类就可以,不需要修改原来的代码逻辑。
缺点:每新增一种缓存方式,类会不断增加,而且逻辑基本一样,在一定程度上造成代码冗余;
两个抽象产品:两个接口。
两个供应商:四个实现类(每个供应商都要生产两种产品,对应接口的两个实现)。
抽象工厂:一个工厂接口(定义两种服务产品的获取方式)。
实际拿产品的工厂:一个工厂实现类。 类似于多个供应商负责提供一系列类型的产品。
假设要为用户提供一个Markdown文本转换为HTML和Word的服务,抽象工厂(AbstractFactory)定义:
public interface AbstractFactory {
// 创建Html文档:
HtmlDocument createHtml(String md);
// 创建Word文档:
WordDocument createWord(String md);
}
两个抽象产品(HtmlDocument和WordDocument):
// Html文档接口:
public interface HtmlDocument {
String toHtml();
void save(Path path) throws IOException;
}
// Word文档接口:
public interface WordDocument {
void save(Path path) throws IOException;
}
两个抽象产品(HtmlDocument和WordDocument)具体实现由专业的供应商来完成,市场上有两家供应商:FastDoc Soft的产品便宜并且转换速度快,GoodDoc Soft的产品贵但转换效果好。决定同时使用这两家供应商的产品,以便给免费用户和付费用户提供不同的服务。以FastDoc Soft的产品实现为例:
public class FastHtmlDocument implements HtmlDocument {
public String toHtml() {
...
}
public void save(Path path) throws IOException {
...
}
}
public class FastWordDocument implements WordDocument {
public void save(Path path) throws IOException {
...
}
}
FastDoc Soft工厂(FastFactory)生产两种产品:
public class FastFactory implements AbstractFactory {
public HtmlDocument createHtml(String md) {
return new FastHtmlDocument(md);
}
public WordDocument createWord(String md) {
return new FastWordDocument(md);
}
}
客户端使用FastDoc Soft的服务:
// 创建AbstractFactory,实际类型是FastFactory:
AbstractFactory factory=new FastFactory();
// 生成Html文档:
HtmlDocument html=factory.createHtml("#Hello\nHello, world!");
html.save(Paths.get(".","fast.html"));
// 生成Word文档:
WordDocument word=factory.createWord("#Hello\nHello, world!");
word.save(Paths.get(".","fast.doc"));
单例模式只创建一个实例,节省系统资源开销。
饿汉式在类初始化的时候创建对象,不会存在线程安全问题。代码如下:
public class Singleton {
private static volatile Singleton s = new Singleton();//保证 instance 在所有线程中同步
private Singleton() {//private 避免类在外部被实例化
}
private static Singleton getInstance() {
return s;
}
}
懒汉式在第一次使用时创建对象,多线程环境下要考虑线程安全问题。代码如下:
/**
* @Description 懒汉式 write by xj on date 2021/2/4
*/
public class Singleton {
private static Singleton s;
private Singleton() {
System.out.println("剑圣盖聂");
}
// 线程不安全
private static Singleton getInstance() {
if (null == s) {
s = new Singleton();
}
return s;
}
// 线程安全的
private static Singleton getInstance1() {
synchronized (Singleton.class) {
if (null == s) {
s = new Singleton();
}
}
return s;
}
// 测试类
public static void main(String[] args) {
// 创建20个线程1000个请求去调用单例模式懒汉式创建对象的方法,来验