目录
目录
一、设计模式简介
1、设计模式是什么
2、设计模式的种类
3、设计模式的六大原则
二、创建型模式
1、工厂模式
2、抽象工厂模式
3、单例模式
3.1、单例模式的优缺
3.2、单例创建方式
3.3、饿汉式
3.4、懒汉式
3.4、静态内部类式
3.5、枚举单例
3.6、双检锁式
三、行为型模式
3.1、责任链模式
3.1.1、实现流程
我们常说的设计模式有23种分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。除此之外还有另一类设计模式,就是J2EE 设计模式,有8种。
J2EE设计模式:特别关注表示层。这些模式是由 Sun Java Center 鉴定的。
并发型模式(Concurrency Patterns):关注多线程环境下的并发操作和资源共享问题。这些模式帮助解决线程安全、同步和协调等并发编程的挑战。
1、开闭原则(Open Close Principle)
开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3、依赖倒转原则(Dependence Inversion Principle)
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
5、迪米特法则,又称最少知道原则(Demeter Principle)
最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。实现了创建者和调用者分离,工厂模式分为简单工厂、工厂方法。
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
1.1、Spring开发中的工厂设计模式
(1)Spring IOC
(2)为什么Spring IOC要使用工厂设计模式创建Bean呢
1.2、简单工厂
(1)什么是简单工厂模式
(2)代码演示
步骤1:创建一个接口:
public interface Shape {
void draw();
}
步骤2:创建两个实现接口的实体类
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
步骤3:创建一个工厂,生成基于给定信息的实体类的对象。
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
}
return null;
}
}
步骤4:使用该工厂,通过传递类型信息来获取实体类的对象。
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
}
}
步骤5:程序输出结果。
Inside Circle::draw() method.
Inside Rectangle::draw() method.
1.3、工厂方法模式
(1)什么是工厂方法模式
工厂方法模式Factory Method,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节
(2)代码演示
public interface Car {
public void run();
}
2. 创建工厂方法调用接口(所有的产品需要new出来必须继承他来实现方法)
public interface CarFactory {
Car createCar();
}
3. 创建工厂的产品(奥迪)
public class AoDi implements Car {
public void run() {
System.out.println("我是奥迪汽车..");
}
}
4. 创建工厂另外一种产品(宝马)
public class Bmw implements Car {
public void run() {
System.out.println("我是宝马汽车...");
}
}
5.创建工厂方法调用接口的实例(奥迪)
public class AoDiFactory implements CarFactory {
public Car createCar() {
return new AoDi();
}
}
6. 创建工厂方法调用接口的实例(宝马)
public class BmwFactory implements CarFactory {
public Car createCar() {
return new Bmw();
}
}
7. 演示创建工厂的具体实例
public class Client {
public static void main(String[] args) {
Car aodi = new AoDiFactory().createCar();
Car jili = new BmwFactory().createCar();
aodi.run();
jili.run();
}
}
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。其他工厂负责创建具体的产品。该超级工厂又称为其他工厂的工厂。
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决:主要解决接口选择的问题。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
抽象工厂模式包含的角色:
以形状为类的级别、颜色为族来实例分析实现:
等级抽象
进行等级抽象我们需要将不同的形状声明为抽象类(等级划分)并实现公共的抽象接口(Shape),然后具体的实现类继承自对应的抽象类;
//形状公共接口
public interface Shape {
void draw();
}
//圆形抽象类Circle
public abstract class Circle implements Shape {
public abstract void draw();
}
//长方形抽象类Rectange
public abstract class Rectange implements Shape {
public abstract void draw();
}
//其他图形抽象类... ...
具体的实现类继承自对应的抽象类,继承自不同的抽象类就相当于将类划分为不同的等级,如:
//具体颜色的Circle实现
public class BlueCircle extends Circle {
@Override
public void draw() {
System.out.println("绘制蓝色的圆");
}
}
public class RedCircle extends Circle {
@Override
public void draw() {
System.out.println("绘制红色的圆");
}
}
//具体颜色的Rectange实现
public class RedRectange extends Rectange{
@Override
public void draw() {
System.out.println("绘制红色长方形");
}
}
public class BlueRectange extends Rectange {
@Override
public void draw() {
System.out.println("绘制蓝色长方形");
}
}
具体类族抽象
具体类族的划分我们以颜色为基础,不同类族的对象我们通过对应的具体工厂来创建。所以首先我们需要定义一个抽象工厂,具体工厂(族)实现抽象工厂的方法来生成一组具体对象。
//抽象工厂ShapeFactory
public interface ShapeFactory {
Shape getCircle();
Shape getRectange();
}
//RedShapeFactory(他所代表的是红色形状这一族)
public class RedShapeFactory implements ShapeFactory {
@Override
public Shape getCircle() {
return new RedCircle();
}
@Override
public Shape getRectange() {
return new RedRectange();
}
}
//BlueShapeFactory(他所代表的是兰色形状这一族)
public class BlueShapeFactory implements ShapeFactory {
@Override
public Shape getCircle() {
return new BlueCircle();
}
@Override
public Shape getRectange() {
return new BlueRectange();
}
}
//...其他族...
等级以及族类划分完成之后,我么的客户端在使用时只需要知道抽象工厂(ShapeFactory )以及具体工厂(如BlueShapeFactory )就可以获得指定族的所有形状。
public class TestDemo {
public static void main(String[] args) {
ShapeFactory redShapeFactory = new RedShapeFactory();
Shape circle = redShapeFactory.getCircle();
circle.draw();
Shape rectangle = redShapeFactory.getRectange();
rectangle.draw();
ShapeFactory greenShapeFactory = new GreenShapeFactory();
Shape greenCircle = greenShapeFactory.getCircle();
greenCircle.draw();
}
}
根据我们上面对抽象工厂模式的描述,我们大致能够明白,如果我们在上例的基础上想要增加绿色或者其他颜色的图形时,我们只需要增加一个绿色产品工厂(GreenShapeFactory),同时继承不同的图形抽象类实现不同颜色的图形具体类即可,看起来如下:
//GreenShapeFactory
public class GreenShapeFactory implements ShapeFactory {
@Override
public Shape getCircle() {
return new GreenCircle();
}
@Override
public Shape getRectange() {
return new GreenRectange();
}
}
//GreenRectange
public class GreenRectange extends Rectange {
@Override
public void draw() {
System.out.println("绘制绿色长方形");
}
}
//GreenCircle
public class GreenCircle extends Circle{
@Override
public void draw() {
System.out.println("绘制绿色圆");
}
}
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
优点:
缺点:
单例模式使用注意事项:
单例防止反射漏洞攻击:
private static boolean flag = false;
private Singleton() {
if (flag == false) {
flag = !flag;
} else {
throw new RuntimeException("单例模式被侵犯!");
}
}
public static void main(String[] args) {
}
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
懒汉式也可以分为线程安全和不安全两种实现方式。
线程不安全:
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全:
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
是否多线程安全:是
实现难度:一般
描述:这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟饿汉式方式不同的是:饿汉式方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比饿汉式方式就显得很合理。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
是否多线程安全:是
实现难度:易
描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
是否多线程安全:是
实现难度:较复杂
描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
双重重检测是为了防止多线程下重复初始化实例,所以要防止指令重排序。
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;
}
}
行为型模式关注对象之间的通信和协作,以实现更灵活、松耦合的设计。这些模式帮助我们定义对象之间的交互方式,以便更好地组织代码并实现系统的功能。
责任链模式(Chain of Responsibility): 使多个对象都有机会处理同一请求,从而避免请求的发送者和接受者之间的耦合关系,每个对象都是一个处理节点,将这些对象连成一条链,并沿着这条链传递该请求。
使用场景:
总之,责任链模式适用于需要将请求发送到一系列处理者中的系统,这些处理者可以根据自己的条件来决定是否处理请求。
举例应用说明:
比如一个请求进来之后,需要对请求传递的对象进行多个不同的处理,那个可以将多个不同的处理划分到多个节点中,某个节点只做部分处理,然后将对象传到下一个节点中,就像一个单链表一样,每个节点拿着后一个节点位置。
例如参数传递一个两个参数,第一步校验两个值是否在[1,5]范围内,第二步算两个参数相加的值,第三步算两个参数相乘的值,第四步将相乘的值减去相加的值,判断是否大于零。
1、用到了一个实体类,先创建实体类
@Data
public class ParamEntry {
private Integer param1;
private Integer param2;
/**
* 相乘
*/
private Integer multiply;
/**
* 相加
*/
private Integer addition;
/**
* 相减
*/
private Integer subtract;
}
2、定义抽象类,作为职责步骤的父类
public abstract class CorStep {
/** 下一个规则节点 */
public CorStep nextStep;
/**
* 本规则节点的职责,需要做的处理
* @param paramEntry
*/
public abstract void handleStep(ParamEntry paramEntry);
}
3、定义职责类,实现抽象类,完成每个职责节点的具体任务。
第一步节点的实现类
@Component
public class CheckParameterValueStep extends CorStep {
@Override
public void handleStep(ParamEntry paramEntry) {
//判断两个参数值是否在【1,5】中
Integer param1 = paramEntry.getParam1();
Integer param2 = paramEntry.getParam2();
if (param1 < 1 || param1 > 5){
return;
}
if (param2 < 1 || param2 > 5){
return;
}
nextStep.handleStep(paramEntry);
}
}
第二步节点的实现抽象类,完成相加
@Component
public class AdditionByParamStep extends CorStep {
/**
* 两个参数相加
* @param paramEntry
*/
@Override
public void handleStep(ParamEntry paramEntry) {
Integer param1 = paramEntry.getParam1();
Integer param2 = paramEntry.getParam2();
paramEntry.setAddition(param1+param2);
nextStep.handleStep(paramEntry);
}
}
第三步继承抽象类实现handleStep方法执行相乘
@Component
public class MultiplyByParamStep extends CorStep{
/**
* 将两个参数相乘
* @param paramEntry
*/
@Override
public void handleStep(ParamEntry paramEntry) {
Integer param1 = paramEntry.getParam1();
Integer param2 = paramEntry.getParam2();
paramEntry.setMultiply(param1*param2);
//调用下一个节点的处理任务方法
nextStep.handleStep(paramEntry);
}
}
第四步,在本次测试的责任链中只有四个步骤,所以这也是左后异步,依旧是继承抽象类,实现handleStep方法,在方法的末尾,不再调用下一个节点。
@Component
public class SubtractByMulAndAddStep extends CorStep {
@Override
public void handleStep(ParamEntry paramEntry) {
Integer multiply = paramEntry.getMultiply();
Integer addition = paramEntry.getAddition();
paramEntry.setSubtract(multiply-addition);
System.out.println("最终处理完成:"+paramEntry.toString());
}
}
4、定义职责类的持有者类,创建方法作为调用的接口,并执行责任链的处理顺序,
/**
* 责任链模式
* 1 指定责任链的顺序,每个节点拿着下一个节点的位置,最后一个节点的nextStep为空。
* 2 调用责任链的开头
*/
@Component
public class CorStepHolder {
private CorStep firstStep;
@Resource
AdditionByParamStep additionByParamStep;
@Resource
CheckParameterValueStep checkParameterValueStep;
@Resource
MultiplyByParamStep multiplyByParamStep;
@Resource
SubtractByMulAndAddStep subtractByMulAndAddStep;
/**
* 编排步骤,在类对象创建时
*/
@PostConstruct
private void init(){
//第一步,执行checkParameterValueStep
firstStep = checkParameterValueStep;
//第二步,为第一步的下一节点指定第二步
checkParameterValueStep.nextStep = multiplyByParamStep;
//第三步,为第二步的下一节点指定第三步
multiplyByParamStep.nextStep = additionByParamStep;
//第四步,为第三步指定下一节点的对象
additionByParamStep.nextStep = subtractByMulAndAddStep;
}
/**
* 执行任务链
* @param paramEntry
*/
public void executionStep(ParamEntry paramEntry){
if (!StringUtils.isEmpty(paramEntry)){
if (paramEntry.getParam1() != null && paramEntry.getParam2() != null){
firstStep.handleStep(paramEntry);
}
}
}
}
总结
责任链模式的实现总共需要三种类,一种是抽象类,作为所有节点类的父类;一种是节点类,继承父类中“具体处理责任”的方法;第三中类时持有责任链的类,在该类中首先定义责任链的顺序,然后调用首节点。