软件中既有变化的部分又有稳定的部分,设计模式总结了几种抽象出稳定部分的方式,让我们能通过设计模式尽量使代码符合设计模式原则(开闭原则、单一职责原则、依赖倒置原则、里氏替换原则、接口隔离原则)
设计模式分类
创建型:抽象和封装对象创建过程,客户程序不再关注对象的具体创建,只需要去使用这些创造好的对象。包括:抽象工厂模式、工厂方法模式、单例模式、创建者模式、原型模式。
结构型:怎么样组装现有的类,设计他们的交互模式,以便达到某种功能。包括:适配器模式、桥接模式、组合模式、装饰模式、门面模式、享元模式、代理模式。
行为型:涉及到算法和类的职责分配,描述了对象和类之前的通信模式并且刻画了程序运行时难以跟踪的复杂控制流。包括:责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、状态模式、策略模式、模板方法模式、观察者模式、访问者模式。
设计模式原则
单一职责原则
一个类或模块只负责一个职责,不要承担过多任务。原则上我们在设计类的时候不要设计大而全的类,要设计功能单一的类。实际开发时不必严格遵守,等业务发展到一定程度再进行拆分
什么时候需要重构或者设计
1、类依赖过多其他类
2、类提供的功能与其名字不想匹配
3、类的代码函数过多
开闭原则
多扩展开放,对修改关闭。添加一个功能应该是在已有代码基础上进行扩展,而不是修改已有的代码。
开闭原则需要我们在设计代码时预留扩展点,而扩展点的考虑需要结合业务以及用户使用方式、版本升级等等。当然如果为了预留扩展点导致代码开发难度大大提高,这也得不偿失,因此也需要我们进行权衡。
里氏替换原则
子类能够替换程序中父类对象出现的任何地方,并且保证原来的程序的逻辑行为不变以及正确性不会被破坏。
接口隔离原则
客户端不应该依赖它不需要的接口。(和单一职责有点类似,但这个是接口层面)
依赖倒置原则
高层模块(调用者)不要依赖底层模块(被调用者),高层模块和底层模块应该通过抽象来相互依赖。抽象不要依赖具体实现细节,具体实现细节依赖抽象。如Spring框架中的Aware接口,框架依赖Aware接口给予具体的视线增加功能,但是通过接口来获取功能,而不是直接耦合实现。
设计模式详解
这里说明的设计模式并非全部23种设计模式
创建型
单例模式
定义
保持一个类只有一个实例,并且提供一个全局访问点
应用场景
spring的单例模式、线程池、数据库连接池
创建方法
只列举了部分
懒汉模式
双重检查加锁
class LazySingleton {
//volatile防止指令重排
private static volatile LazySingleton lazySingleton;
//无法防止反射攻击
private LazySingleton(){
}
public static LazySingleton getInstance() {
if (lazySingleton == null) {
synchronized (LazySingleton.class) {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
}
饿汉模式
class HungrySingleton{
//通过JVM类加载来保证单例以及线程安全,在初始化阶段给类的静态变量赋值
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton(){
//防止反射攻击
if (instance != null) {
throw new RuntimeException("单例不允许多个实例");
}
}
public static HungrySingleton getInstance() {
return instance;
}
}
测试
public class SingletonDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 20; i++) {
System.out.println(LazySingleton.getInstance());
}
}
}
应用场景
线程池、数据库连接池、spring单例模式
优点
工厂方法模式
定义
定义了一个用于创建对象的接口,让子类决定实例化哪个类,将创建对象延迟到子类
创建方法
package edu.wyn.designpattern;
abstract class Application {
abstract Product createObject();
Product getObject() {
return createObject();
}
}
class ConcreteProductA extends Application {
@Override
Product createObject() {
return new ProductA();
}
}
class ConcreteProductB extends Application {
@Override
Product createObject() {
return new ProductB();
}
}
interface Product {
void printName();
}
class ProductA implements Product {
@Override
public void printName() {
System.out.println("this is product a");
}
}
class ProductB implements Product {
@Override
public void printName() {
System.out.println("this is product b");
}
}
public class FactoryMethodDemo {
public static void main(String[] args) {
Application application = new ConcreteProductA();
application.createObject().printName();
Application application2 = new ConcreteProductB();
application2.createObject().printName();
}
}
应用场景
1、不知道使用对象的确切类型
2、希望为库或者框架提供扩展其内部组件方法时
优点
1、将具体产品和创建者解耦
2、符合单一职责原则
3、符合开闭原则
抽象工厂模式
定义
提供一个创建一系列相关或互相依赖对象的接口,而无需指定他们具体的类
创建方法
package edu.wyn.designpattern;
public class AbstractFactoryDemo {
public static void main(String[] args) {
IDatabaseUtils iDatabaseUtils = new MysqlDataBaseUtils();
IConnection connection = iDatabaseUtils.getConnection();
connection.connect();
ICommand command = iDatabaseUtils.getCommand();
command.command();
}
}
interface IConnection{
void connect();
}
class MysqlConnection implements IConnection {
@Override
public void connect() {
System.out.println("mysql connection...");
}
}
interface ICommand{
void command();
}
class MysqlCommand implements ICommand {
@Override
public void command() {
System.out.println("mysql command...");
}
}
interface IDatabaseUtils{
IConnection getConnection();
ICommand getCommand();
}
class MysqlDataBaseUtils implements IDatabaseUtils {
@Override
public IConnection getConnection() {
return new MysqlConnection();
}
@Override
public ICommand getCommand() {
return new MysqlCommand();
}
}
应用场景
程序需要处理不同系列的相关产品,但是我们不希望它依赖于这些产品的具体类的时候,可以使用抽象工厂
优点
1、可以确定从工厂中得到的产品是彼此兼容的
2、可以避免具体产品和客户端代码之间的紧密耦合
3、符合单一职责原则
4、符合开闭原则
与工厂方法的区别
相当于将一组相关的application(工厂方法)放在了一个接口中
建造者模式
定义
将一个复杂对象的创建和它的表示分离,使得同样的创建过程可以创建不同的表示
创建方法
lombok的builder
应用场景
1、需要生成的对象具有复杂的内部结构
2、需要生成的对象内部属性本身相互依赖
3、与不可变对象配合使用
优点
1、建造者独立,易扩展
2、便于控制细节风险
原型模式
定义
创建对象的种类,然后通过拷贝这些原型创建新的对象
创建方法
实现Cloneable接口并且重写clone方法,要注意引用类型是浅拷贝,要让引用类型也实现Cloneable
应用场景
代码不需要依赖于需要复制的具体类
优点
1、不耦合具体类的情况下克隆对象
2、避免重复的初始化代码
3、更方便构造复杂对象
结构型
适配器模式
定义
将类的接口转换为客户端希望的另一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
创建方法
对象适配器--组合
类--继承
应用场景
1、希望使用某些现有类,但其接口与我们其他代码不兼容
2、当你希望重用几个现有的类,但这些类缺少一些不能添加到超类中的公共功能时
优点
1、符合单一职责原则
2、符合开闭原则
装饰模式
定义
在不改变原有对象的基础上,将功能附加到对象上
创建方法
应用场景
扩展一个类的功能,或者给一个类添加附加职责
优点
1、不改变原有对象的情况下给一个对象扩展功能
2、使用不同的组合可以实现不同的效果
3、符合开闭原则
享元模式
定义
运用共享技术有效支持大量细粒度对象
优点
若系统有大量类似对象,可以节省大量的内存和CPU,比如字符串常量池
门面模式
定义
为子系统中的一组接口提供一个一致的接口,门面模式定义了一个高层接口,这个接口使得这一子系统更容易使用
应用场景
1、需要使用复杂子系统有限但直接的接口
2、想要将子系统组织成层
3、微服务时对外提供的接口
优点
简化客户端调用
行为型
责任链模式
定义
为请求创建了一个接收者对象的链
应用场景
对输入对象需要进行多种校验
优点
1、请求的发送者和接收者解耦
2、可以控制执行顺序
3、符合开闭原则和单一职责原则
策略模式
定义
定义了算法族,分别封装起来,让它们之间可以互相替换,此模式的变化独立于算法的使用者
应用场景
优点
1、符合开闭原则
2、符合单一职责原则
模板方法模式
定义
定义了一个操作的算法骨架,而将一些步骤延迟到子类中。子类在不改变整个算法框架的基础上可以重定义算法的某些特定步骤
创建方法
继承、多态
应用场景
spring中扫描bean的方法
优点
1、符合开闭原则
观察者模式
定义
定义了对象之间的一对多的依赖,让多个观察者对象同时监听某个主题对象,当主题对象发生改变的时候,它的所有依赖者都会收到通知并更新
优点
1、符合开闭原则
2、可以在运行时建立对象之间的联系
后记
在我自己的实际使用中,常常将几种设计模式混杂在一起中,比如将策略模式、模板方法模式等混合在一起。并不一定要拘泥于某项代码的写法。