一 什么是设计模式

设计模式是一套反复使用的代码设计总结。使用设计模式是为了可重用代码、保证代码可靠性、程序的重用性。熟悉设计模式能更容易看懂框架源码,更好的设计自己的系统。

二 设计模式分类

设计模式分为创建型、结构型和行为型三种模式。

三 设计模式的六大原则

开放封闭原则:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化

里氏代换原则:使用的基类可以在任何地方使用继承的子类,完美的替换基类。子类可以扩展父类的功能,但不能改变父类原有的功能。子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法【模版模式的钩子方法不在此列】,子类中可以增加自己特有的方法

依赖倒转原则:面向接口编程,我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类

接口隔离原则:使用多个隔离的接口,比使用单个接口要好

迪米特法则:一个类尽量减少自己对其他对象的依赖,简称类间解耦

单一职责原则:一个方法只负责一件事情

四 常用的设计模式

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code