一 什么是设计模式
设计模式是一套反复使用的代码设计总结。使用设计模式是为了可重用代码、保证代码可靠性、程序的重用性。熟悉设计模式能更容易看懂框架源码,更好的设计自己的系统。
二 设计模式分类
设计模式分为创建型、结构型和行为型三种模式。
三 设计模式的六大原则
开放封闭原则:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化
里氏代换原则:使用的基类可以在任何地方使用继承的子类,完美的替换基类。子类可以扩展父类的功能,但不能改变父类原有的功能。子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法【模版模式的钩子方法不在此列】,子类中可以增加自己特有的方法
依赖倒转原则:面向接口编程,我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类
接口隔离原则:使用多个隔离的接口,比使用单个接口要好
迪米特法则:一个类尽量减少自己对其他对象的依赖,简称类间解耦
单一职责原则:一个方法只负责一件事情
四 常用的设计模式
4.1 单例模式
使用场景:
网站计数器、应用程序日志应用、线程池
注意事项:
注意线程安全问题
代码实现:
1 package com.leo.basic.code.design; 2 3 /** 4 * @DESCRIPTION 5 * 单例模式:采用一定的方式保证一个类在一个系统中只有一个实例 6 * 优点:单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能 7 * 缺点:由于单利模式中没有抽象层,因此单例类的扩展有很大的困难;不适用于变化的对象 8 * @DATE 2022-11-02 9 * @AUTHOR liuzhilong 10 */ 11 public class SingleTest { 12 13 public static void main(String[] args) { 14 // 1、饿汉式单例模式(静态常量) 15 Singleton1 singleton1 = Singleton1.getInstance(); 16 Singleton1 singleton2 = Singleton1.getInstance(); 17 System.out.println(singleton1 == singleton2); 18 // 2、饿汉式单例模式(静态代码块) 19 Singleton2 singleton21 = Singleton2.getInstance(); 20 Singleton2 singleton22 = Singleton2.getInstance(); 21 System.out.println(singleton21 == singleton22); 22 // 3、静态内部类 23 Singleton3 singleton31 = Singleton3.getInstance(); 24 Singleton3 singleton32 = Singleton3.getInstance(); 25 System.out.println(singleton31 == singleton32); 26 } 27 28 29 } 30 // 实现方式 31 // 1、饿汉式单例模式(静态常量) 32 class Singleton1 { 33 34 private static Singleton1 instance = new Singleton1(); 35 36 private Singleton1() { 37 38 } 39 40 public static Singleton1 getInstance() { 41 return instance; 42 } 43 } 44 45 // 2、饿汉式单例模式(静态代码块) 46 class Singleton2 { 47 48 private static Singleton2 instance; 49 50 static { 51 instance = new Singleton2(); 52 } 53 54 private Singleton2() { 55 56 } 57 58 public static Singleton2 getInstance() { 59 return instance; 60 } 61 } 62 63 // 3、静态内部类 64 class Singleton3 { 65 66 private Singleton3() { 67 68 } 69 // 采用了类装载的机制来保证初始化实例时只有一个线程 70 private static class Singleton3Instance{ 71 private static final Singleton3 instance = new Singleton3(); 72 } 73 74 public static Singleton3 getInstance() { 75 return Singleton3Instance.instance; 76 } 77 } 78 79 // 4、枚举 80 enum Singleton4 { 81 INSTANCE; 82 } 83 84 85 // 5、双重检查(Double-check) 86 class Singleton5 { 87 88 private static Singleton5 instance; 89 90 private Singleton5() { 91 92 } 93 // 因为JVM本质重排序的原因,可能会初始化多次,不推荐使用 94 public static Singleton5 getInstance() { 95 if(instance == null){ 96 synchronized (Singleton5.class){ 97 if(instance == null){ 98 instance = new Singleton5(); 99 } 100 } 101 } 102 return instance; 103 } 104 }
4.2 工厂模式
使用场景:
实例化对象、Spring IoC
注意事项:
代码实现:
1 package com.leo.basic.code.design; 2 3 4 5 /** 6 * @DESCRIPTION 工厂模式: 7 * 简单工厂:创建对象在工厂类中,工厂类很重【1类产品】 8 * 工厂方法:创建对象在具体工厂实现类中,工厂是个接口【1类产品】 9 * 抽象工厂:工厂中定义了一系列产品创建方法,创建对象在具体的工厂实现类中【多类产品】 10 * 优点:在创建对象时不会对客户端暴露创建逻辑,实现了创建者和调用者分离 11 * 缺点: 12 * @DATE 2022-11-02 13 * @AUTHOR liuzhilong 14 */ 15 public class FactoryTest { 16 17 public static void main(String[] args) { 18 // 1、简单工厂 19 Car aodi = SimpleCarFactory.createCar("奥迪"); 20 Car bmw = SimpleCarFactory.createCar("宝马"); 21 aodi.run(); 22 bmw.run(); 23 24 // 2、工厂方法 25 Car aodi1 = new AoDiFactory().createCar(); 26 Car jili1 = new BmwFactory().createCar(); 27 aodi1.run(); 28 jili1.run(); 29 } 30 } 31 32 // Car 产品 33 interface Car { 34 35 void run(); 36 } 37 38 class Bmw implements Car { 39 40 @Override 41 public void run() { 42 System.out.println("Bmw"); 43 } 44 } 45 46 class AoDi implements Car { 47 48 @Override 49 public void run() { 50 System.out.println("AoDi"); 51 } 52 } 53 54 // 1、简单工厂 55 class SimpleCarFactory { 56 57 public static Car createCar(String name) { 58 if ("".equals(name)) { 59 return null; 60 } 61 if (name.equals("奥迪")) { 62 return new AoDi(); 63 } 64 if (name.equals("宝马")) { 65 return new Bmw(); 66 } 67 return null; 68 } 69 } 70 71 72 // 2、工厂方法 73 interface MethodCarFactory { 74 75 Car createCar(); 76 } 77 78 class AoDiFactory implements MethodCarFactory { 79 80 public Car createCar() { 81 return new AoDi(); 82 } 83 } 84 85 class BmwFactory implements MethodCarFactory { 86 87 public Car createCar() { 88 return new Bmw(); 89 } 90 } 91 92 // 3、抽象工厂 93 // 发动机产品 94 interface Engine { 95 void run(); 96 } 97 class EngineA implements Engine { 98 public void run() { 99 System.out.println("转的快!"); 100 } 101 } 102 class EngineB implements Engine { 103 public void run() { 104 System.out.println("转的慢!"); 105 } 106 } 107 108 interface TotalFactory { 109 // 创建汽车 110 Car createChair(); 111 // 创建发动机 112 Engine createEngine(); 113 } 114 // 上海工厂实现类,由他决定调用哪个工厂的那个实例 115 class ShanghaiFactory implements TotalFactory { 116 public Engine createEngine() { 117 return new EngineA(); 118 } 119 public Car createChair() { 120 return new AoDi(); 121 } 122 }
4.3 模版模式
使用场景:
数据库访问的封装、Junit单元测试、servlet中关于doGet/doPost方法的调用
注意事项:
代码实现:
1 package com.leo.basic.code.design; 2 3 /** 4 * @DESCRIPTION 5 * 模版:定义一个操作中的算法骨架(父类),而将一些步骤延迟到子类中,是一个流程!!。主要子类不能覆盖父类的模版方法!!! 6 * 优点:公共的逻辑代码抽取,代码复用;封装不变的部分,重写可变的部分,易扩展 7 * 缺点:每来一个子类就要定义一套子类的规范,项目的体积会越来越大 8 * @DATE 2022-11-02 9 * @AUTHOR liuzhilong 10 */ 11 public class TemplateTest { 12 13 public static void main(String[] args) { 14 RestaurantTemplate restaurantTemplate = new RestaurantLobsterImpl(); 15 restaurantTemplate.process(); 16 } 17 } 18 19 abstract class RestaurantTemplate { 20 21 // 1.看菜单 22 public void menu() { 23 System.out.println("看菜单"); 24 } 25 26 // 2.点菜业务 27 abstract void spotMenu(); 28 29 // 3.吃饭业务 30 public void havingDinner() { 31 System.out.println("吃饭"); 32 } 33 34 // 4.付款业务 35 abstract void payment(); 36 37 // 5.走人 38 public void goR() { 39 System.out.println("走人"); 40 } 41 42 //模板通用结构 43 public void process() { 44 menu(); 45 spotMenu(); 46 havingDinner(); 47 payment(); 48 goR(); 49 } 50 } 51 52 class RestaurantLobsterImpl extends RestaurantTemplate { 53 54 void spotMenu() { 55 System.out.println("龙虾"); 56 } 57 58 void payment() { 59 System.out.println("50块"); 60 } 61 }
4.4 策略模式
使用场景:
策略模式的用意是针对一组算法或逻辑,将每一个算法或逻辑封装到具有共同接口的独立的类中,从而使得它们之间可以相互替换注意事项:
代码实现:
1 package com.leo.basic.code.design; 2 3 /** 4 * @DESCRIPTION 5 * 策略:定义了一系列的算法 或 逻辑 或 相同意义的操作,并将每一个算法、逻辑、操作封装起来,而且使它们还可以相互替换。 6 * 注意策略针对的是一个点,是一个算法,不是流程。针对每个点实现一个具体的实现类,这点和模版方法不同!!! 7 * 优点:1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性非常良好 8 * 缺点:1、策略类会增多。 2、所有策略类都需要对外暴露。 9 * @DATE 2022-11-02 10 * @AUTHOR liuzhilong 11 */ 12 public class StrategyTest { 13 14 public static void main(String[] args) { 15 Context context; 16 //使用支付逻辑A 17 context = new Context(new PayStrategyA()); 18 context.algorithmInterface(); 19 //使用支付逻辑B 20 context = new Context(new PayStrategyB()); 21 context.algorithmInterface(); 22 } 23 } 24 25 abstract class PayStrategy { 26 27 // 支付逻辑方法 28 abstract void algorithmInterface(); 29 } 30 31 class PayStrategyA extends PayStrategy { 32 33 void algorithmInterface() { 34 System.out.println("微信支付"); 35 } 36 } 37 38 class PayStrategyB extends PayStrategy { 39 40 void algorithmInterface() { 41 System.out.println("支付宝支付"); 42 } 43 } 44 45 //定义下文维护算法策略 46 class Context { 47 48 PayStrategy strategy; 49 50 public Context(PayStrategy strategy) { 51 this.strategy = strategy; 52 } 53 54 public void algorithmInterface() { 55 strategy.algorithmInterface(); 56 } 57 }
4.5 适配器模式
使用场景:
注意事项:
代码实现:
1 package com.leo.basic.code.design; 2 3 /** 4 * @DESCRIPTION 5 * 适配器:将一个类的接口转换成客户希望的另外一个接口 6 * 优点:让原本因接口不匹配不能在一起工作的两个类可以协同工作 7 * 缺点: 8 * @DATE 2022-11-03 9 * @AUTHOR liuzhilong 10 */ 11 public class AdapterTest { 12 13 public static void main(String[] args) { 14 // 1、类适配器 15 //电脑,适配器,网线 16 Computer computer = new Computer();//电脑 17 Adapter adapter = new Adapter();//转接器 18 computer.net(adapter);//电脑直接连接转接器就可以 19 20 // 2、对象适配器 21 Adapter2 adapter2 = new Adapter2(new Adaptee());//转接器 22 computer.net(adapter2); 23 24 } 25 } 26 27 // 1、类适配器 28 //要适配的类:网线 -- Adaptee 29 class Adaptee { 30 31 //功能:上网 32 public void request() { 33 System.out.println("链接网线上网"); 34 } 35 } 36 37 //接口转换器的抽象实现 -- target 38 interface NetToUsb { 39 //作用:处理请求,网线=>usb 40 public void handleRequest(); 41 } 42 43 //真正的适配器,余姚链接usb,连接网线 -- Adapter 44 class Adapter extends Adaptee implements NetToUsb{ 45 @Override 46 public void handleRequest() { 47 super.request();//可以上网了 48 } 49 } 50 51 //客户端类:想上网,插不上网线 52 class Computer { 53 //电脑需要连接上转接器才可以上网 54 public void net(NetToUsb adapter){ 55 //上网的具体实现:找一个转接头 56 adapter.handleRequest(); 57 } 58 } 59 60 // 2、对象适配器 61 class Adapter2 implements NetToUsb { 62 private Adaptee adaptee; 63 public Adapter2(Adaptee adaptee){ 64 this.adaptee = adaptee; 65 } 66 @Override 67 public void handleRequest() { 68 adaptee.request();;//可以上网了 69 } 70 }
4.6 建造者模式
使用场景:
需要生成的对象具有复杂的内部结构;需要生成的对象内部属性本身相互依赖
注意事项:
代码实现:
1 package com.leo.basic.code.design; 2 3 /** 4 * @DESCRIPTION 5 * 建造者:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的方式进行创建 6 * 与工厂模式的区别是:建造者模式更加关注零件装配的顺序 7 * 优点: 8 * 缺点: 9 * @DATE 2022-11-02 10 * @AUTHOR liuzhilong 11 */ 12 public class BuilderTest { 13 14 public static void main(String[] args) { 15 PersonDirector pb = new PersonDirector(); 16 Arms arms = pb.constructPerson(new ArmsBuilder()); 17 System.out.println(arms.getHelmet()); 18 System.out.println(arms.getArmor()); 19 System.out.println(arms.getWeapon()); 20 } 21 } 22 23 // 1、Product:要创建的复杂对象 -- 装备类 24 class Arms { 25 //头盔 26 public String helmet; 27 //铠甲 28 public String armor; 29 //武器 30 public String weapon; 31 32 public String getHelmet() { 33 return helmet; 34 } 35 36 public void setHelmet(String helmet) { 37 this.helmet = helmet; 38 } 39 40 public String getArmor() { 41 return armor; 42 } 43 44 public void setArmor(String armor) { 45 this.armor = armor; 46 } 47 48 public String getWeapon() { 49 return weapon; 50 } 51 52 public void setWeapon(String weapon) { 53 this.weapon = weapon; 54 } 55 } 56 57 // 2、Builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造 -- 装备制造规范 58 interface PersonBuilder { 59 void builderHelmetMurder(); 60 void builderArmorMurder(); 61 void builderWeaponMurder(); 62 void builderHelmetYanLong(); 63 void builderArmorYanLong(); 64 void builderWeaponYanLong(); 65 Arms BuilderArms(); //组装 66 } 67 68 // 3、ConcreteBuilder:实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。在建造过程完成后,提供产品的实例 -- 装备制造 69 class ArmsBuilder implements PersonBuilder { 70 private Arms arms; 71 //创建一个Arms实例,用于调用set方法 72 public ArmsBuilder() { 73 arms = new Arms(); 74 } 75 public void builderHelmetMurder() { 76 arms.setHelmet("夺命头盔"); 77 } 78 public void builderArmorMurder() { 79 arms.setArmor("夺命铠甲"); 80 } 81 public void builderWeaponMurder() { 82 arms.setWeapon("夺命宝刀"); 83 } 84 public void builderHelmetYanLong() { 85 arms.setHelmet("炎龙头盔"); 86 } 87 public void builderArmorYanLong() { 88 arms.setArmor("炎龙铠甲"); 89 } 90 public void builderWeaponYanLong() { 91 arms.setWeapon("炎龙宝刀"); 92 } 93 public Arms BuilderArms() { 94 return arms; 95 } 96 97 } 98 99 // 4、Director:调用具体建造者来创建复杂对象的各个部分, 100 // 在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建 101 class PersonDirector { 102 103 //组装 104 public Arms constructPerson(PersonBuilder pb) { 105 pb.builderHelmetYanLong(); 106 pb.builderArmorMurder(); 107 pb.builderWeaponMurder(); 108 return pb.BuilderArms(); 109 } 110 }
4.7 代理模式
使用场景:
Spring AOP、日志打印、异常处理、事务控制、权限控制
注意事项:
代码实现:
1 package com.leo.basic.code.design; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 /** 8 * @DESCRIPTION 9 * 代理:通过代理控制对象的访问,可以在这个对象调用方法之前、调用方法之后去处理/添加新的功能 10 * 静态代理:由程序员创建或工具生成代理类的源码,再编译代理类 11 * 动态代理:动态代理的对象,是利用JDK的API,动态的在内存中构建代理对象 12 * 优点:降低了系统的耦合度,扩展性好,可以起到保护目标对象的作用 13 * 缺点: 14 * @DATE 2022-11-02 15 * @AUTHOR liuzhilong 16 */ 17 public class ProxyTest { 18 19 public static void main(String[] args) { 20 // 1、静态代理 21 UserDao userDao = new UserDaoImpl(); 22 UserDaoProxy userDaoProxy = new UserDaoProxy(userDao); 23 userDaoProxy.save(); 24 25 // 2、动态代理 26 // 被代理对象 27 UserDao userDaoImpl = new UserDaoImpl(); 28 InvocationHandlerImpl invocationHandlerImpl = new 29 InvocationHandlerImpl(userDaoImpl); 30 //类加载器 31 ClassLoader loader = userDaoImpl.getClass().getClassLoader(); 32 Class<?>[] interfaces = userDaoImpl.getClass().getInterfaces(); 33 // 主要装载器、一组接口及调用处理动态代理实例 34 UserDao newProxyInstance = (UserDao) Proxy.newProxyInstance(loader, 35 interfaces, invocationHandlerImpl); 36 newProxyInstance.save(); 37 } 38 } 39 40 // 1、静态代理 41 interface UserDao{ 42 void save(); 43 } 44 class UserDaoImpl implements UserDao { 45 public void save() { 46 System.out.println("保存数据方法"); 47 } 48 } 49 50 class UserDaoProxy extends UserDaoImpl { 51 private UserDao userDao; 52 public UserDaoProxy(UserDao userDao) { 53 this.userDao = userDao; 54 } 55 public void save() { 56 System.out.println("开启事物..."); 57 userDao.save(); 58 System.out.println("关闭事物..."); 59 } 60 } 61 62 // 2、动态代理 63 class InvocationHandlerImpl implements InvocationHandler { 64 // 这其实业务实现类对象,用来调用具体的业务方法 65 private Object target; 66 // 通过构造函数传入目标对象 67 public InvocationHandlerImpl(Object target) { 68 this.target = target; 69 } 70 //动态代理实际运行的代理方法 71 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 72 System.out.println("调用开始处理"); 73 //下面invoke()方法是以反射的方式来创建对象,第一个参数是要创建的对象,第二个是构成方法的参数,由第二个参数来决定创建对象使用哪个构造方法 74 Object result = method.invoke(target, args); 75 System.out.println("调用结束处理"); 76 return result; 77 } 78 }
4.8 观察者模式
使用场景:
关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。事件多级触发场景
注意事项:
代码实现:
1 package com.leo.basic.code.design; 2 3 import java.util.Vector; 4 5 /** 6 * @DESCRIPTION 7 * 观察者:是一种行为型设计模式。最重要的作用就是解耦!将观察者与被观察者解耦,使得他们之间的依赖性更小 8 * 优点:降低了目标与观察者之间的耦合关系 9 * 缺点:目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用;当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率 10 * @DATE 2022-11-02 11 * @AUTHOR liuzhilong 12 */ 13 public class ObserverTest { 14 15 public static void main(String[] args) { 16 // 目标对象 17 ConcreteSubject subject = new ConcreteSubject(); 18 // 创建多个观察者 19 ObserverImpl obs1 = new ObserverImpl(); 20 ObserverImpl obs2 = new ObserverImpl(); 21 ObserverImpl obs3 = new ObserverImpl(); 22 // 注册到观察队列中 23 subject.registerObserver(obs1); 24 subject.registerObserver(obs2); 25 subject.registerObserver(obs3); 26 // 改变State状态 27 subject.setState(300); 28 System.out.println("obs1观察者的MyState状态值为:"+obs1.getMyState()); 29 System.out.println("obs2观察者的MyState状态值为:"+obs2.getMyState()); 30 System.out.println("obs3观察者的MyState状态值为:"+obs3.getMyState()); 31 // 改变State状态 32 subject.setState(400); 33 System.out.println("obs1观察者的MyState状态值为:"+obs1.getMyState()); 34 System.out.println("obs2观察者的MyState状态值为:"+obs2.getMyState()); 35 System.out.println("obs3观察者的MyState状态值为:"+obs3.getMyState()); 36 } 37 } 38 39 // 1、定义抽象观察者,每一个实现该接口的实现类都是具体观察者 40 interface Observer { 41 // 观察者方法 42 void update(int state); 43 } 44 45 // 2、定义具体观察者 46 class ObserverImpl implements Observer { 47 // 具体观察者的属性 48 private int myState; 49 public void update(int state) { 50 myState=state; 51 System.out.println("收到消息,myState值改为:"+state); 52 } 53 public int getMyState() { 54 return myState; 55 } 56 } 57 58 // 3、定义主题。主题定义观察者数组,并实现增、删及通知操作 59 class Subjecct { 60 //观察者的存储集合,不推荐ArrayList,线程不安全, 61 private Vector<Observer> list = new Vector<>(); 62 // 注册观察者方法 63 public void registerObserver(Observer obs) { 64 list.add(obs); 65 } 66 // 删除观察者方法 67 public void removeObserver(Observer obs) { 68 list.remove(obs); 69 } 70 // 通知所有的观察者更新 71 public void notifyAllObserver(int state) { 72 for (Observer observer : list) { 73 observer.update(state); 74 } 75 } 76 } 77 78 // 4、定义具体的业务触发状态变更的地方,他继承继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多 79 class ConcreteSubject extends Subjecct { 80 //被观察对象的属性 81 private int state; 82 public int getState(){ 83 return state; 84 } 85 public void setState(int state){ 86 this.state=state; 87 //主题对象(目标对象)值发生改变 88 this.notifyAllObserver(state); 89 } 90 }