Java设计模式
一.概念
1.什么是设计模式?
设计模式是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。
2.目的?
1)代码重用性(即:相同功能代码,不用多次编写)
2)可读性 (即:编程规范性,便于其他程序员阅读和理解)
3)可扩展性 (即:当需要增加功能时,非常方便)
4)可靠性 (即:增加新功能后,对原来功能没影响)
5)使程序高内聚,低耦合
二.七大原则
1.单一职责原则
1)概念
即一个类应该只负责一项职责
2)实例
动物的运动方式
1 public class SingleDemo1 { 2 public static void main(String[] args) { 3 Animal vehicle =new Animal(); 4 vehicle.run("马"); 5 vehicle.run("鸟"); 6 vehicle.run("鱼"); 7 } 8 } 9 10 //动物类 11 class Animal { 12 public void run(String vehicel) { 13 System.out.println(vehicel+" 在公路上跑"); 14 } 15 }
demo1
1.run方法违反了单一职责原则
2.解决思路,根据不同动物对运动方式,分解成不同类
1 public class SingleDemo2 { 2 public static void main(String[] args) { 3 RoadAnimal roadAnimal = new RoadAnimal(); 4 roadAnimal.run("马"); 5 6 WaterAnimal waterAnimal = new WaterAnimal(); 7 waterAnimal.run("鱼"); 8 9 AirAnimal airAnimal = new AirAnimal(); 10 roadAnimal.run("鸟"); 11 12 } 13 } 14 15 //demo2 16 class RoadAnimal { 17 public void run(String vehicel) { 18 System.out.println(vehicel+" 在公路上跑"); 19 } 20 } 21 class WaterAnimal { 22 public void run(String vehicel) { 23 System.out.println(vehicel+" 在水里游"); 24 } 25 } 26 class AirAnimal { 27 public void run(String vehicel) { 28 System.out.println(vehicel+" 在天上飞"); 29 } 30 }
demo2
1.遵守了单一职责原则
2.改动很大,类分解
3.还可以怎么改?
1 public class SingleDemo3 { 2 public static void main(String[] args) { 3 NewAnimal newAnimal = new NewAnimal(); 4 newAnimal.run("马"); 5 newAnimal.runWater("鱼"); 6 newAnimal.runAir("鸟"); 7 } 8 } 9 10 //demo3 11 class NewAnimal { 12 public void run(String vehicel) { 13 System.out.println(vehicel+" 在公路上跑"); 14 } 15 public void runWater(String vehicel) { 16 System.out.println(vehicel+" 在水里游"); 17 } 18 public void runAir(String vehicel) { 19 System.out.println(vehicel+" 在天上飞"); 20 } 21 }
demo3
1.只是在原来方法上添加方法
2.在类上没有遵守单一职责原则,但在方法上,仍然遵守单一职责原则
3)注意点
降低类复杂度,一个类只负责一项职责
提高可读性,可维护性
降低变更引起的风险
一般情况下,要遵守单一职责原则,只有逻辑特别简单,才可以在类级别违反单一职责原则,但至少保证方法的单一职责原则
2.接口隔离原则
1)概念
客户端不应该依赖它不需要的接口,即一个类对另一个类依赖建立在最小对接口上
2)实例
类A通过接口依赖类C,类B通过接口依赖D
改进前
1 public class SegregationDemo1 { 2 public static void main(String[] args) { 3 // 写个demo 类A通过接口依赖类C 类B通过接口依赖类D 4 // 以下方法不符合接口隔离原则 5 A a = new A(); 6 B b = new B(); 7 a.do1(new C()); 8 a.do2(new C()); 9 a.do3(new C()); 10 b.do1(new D()); 11 b.do4(new D()); 12 b.do5(new D()); 13 } 14 } 15 16 // 定义接口 有方法1,2,3,4,5 17 interface Interface1{ 18 void method1(); 19 void method2(); 20 void method3(); 21 void method4(); 22 void method5(); 23 } 24 25 // 类C实现接口 26 class C implements Interface1{ 27 28 public void method1() { 29 System.out.println("C 实现method1"); 30 } 31 32 public void method2() { 33 System.out.println("C 实现method2"); 34 } 35 36 public void method3() { 37 System.out.println("C 实现method3"); 38 } 39 40 public void method4() { 41 System.out.println("C 实现method4"); 42 } 43 44 public void method5() { 45 System.out.println("C 实现method5"); 46 } 47 } 48 49 // 类D实现接口 50 class D implements Interface1 { 51 52 public void method1() { 53 System.out.println("D 实现method1"); 54 } 55 56 public void method2() { 57 System.out.println("D 实现method2"); 58 } 59 60 public void method3() { 61 System.out.println("D 实现method3"); 62 } 63 64 public void method4() { 65 System.out.println("D 实现method4"); 66 } 67 68 public void method5() { 69 System.out.println("D 实现method5"); 70 } 71 } 72 73 // 类A 依赖 C的方法1,2,3 74 class A { 75 76 public void do1(Interface1 i) { 77 i.method1(); 78 } 79 80 public void do2(Interface1 i) { 81 i.method2(); 82 } 83 84 public void do3(Interface1 i) { 85 i.method3(); 86 } 87 88 89 } 90 91 // 类B 依赖 D的方法1,4,5 92 class B { 93 94 public void do1(Interface1 i) { 95 i.method1(); 96 } 97 98 public void do4(Interface1 i) { 99 i.method4(); 100 } 101 102 public void do5(Interface1 i) { 103 i.method5(); 104 } 105 }
改进后
1 public class SegregationDemo2 { 2 public static void main(String[] args) { 3 // 改进 4 A1 a = new A1(); 5 B1 b = new B1(); 6 a.do1(new C1()); 7 a.do2(new C1()); 8 a.do3(new C1()); 9 b.do1(new D1()); 10 b.do4(new D1()); 11 b.do5(new D1()); 12 } 13 } 14 15 // 定义接口11 有方法1 16 interface Interface11{ 17 void method1(); 18 } 19 20 // 定义接口12 有方法2,3 21 interface Interface12{ 22 void method2(); 23 void method3(); 24 } 25 26 // 定义接口13 有方法4,5 27 interface Interface13{ 28 void method4(); 29 void method5(); 30 } 31 32 // 类C实现接口11和12 33 class C1 implements Interface11,Interface12{ 34 35 public void method1() { 36 System.out.println("C 实现method1"); 37 } 38 39 public void method2() { 40 System.out.println("C 实现method2"); 41 } 42 43 public void method3() { 44 System.out.println("C 实现method3"); 45 } 46 } 47 48 // 类D实现接口11,13 49 class D1 implements Interface11,Interface13 { 50 51 public void method1() { 52 System.out.println("D 实现method1"); 53 } 54 55 public void method4() { 56 System.out.println("D 实现method4"); 57 } 58 59 public void method5() { 60 System.out.println("D 实现method5"); 61 } 62 } 63 64 // 类A 依赖 C的方法1,2,3 65 class A1 { 66 67 public void do1(Interface11 i) { 68 i.method1(); 69 } 70 71 public void do2(Interface12 i) { 72 i.method2(); 73 } 74 75 public void do3(Interface12 i) { 76 i.method3(); 77 } 78 79 80 } 81 82 // 类B 依赖 D的方法1,4,5 83 class B1 { 84 85 public void do1(Interface11 i) { 86 i.method1(); 87 } 88 89 public void do4(Interface13 i) { 90 i.method4(); 91 } 92 93 public void do5(Interface13 i) { 94 i.method5(); 95 } 96 }
3.依赖倒置原则
1)概念
程序要依赖抽象接口,不用依赖于具体实现
2)实例
1 public class InversionDemo1 { 2 public static void main(String[] args) { 3 Person person = new Person(); 4 person.receive(new Email()); 5 } 6 } 7 8 class Email { 9 public String getMessage(){ 10 return "获得邮件"; 11 } 12 } 13 14 class Person { 15 public void receive(Email e){ 16 System.out.println(e.getMessage()); 17 } 18 }
demo1 简单容易
但是如果获取对象是微信,短信,那么需要新增类,同时Person类也需要增加相应接收方法。怎么改进???
1 public class InversionDemo2 { 2 public static void main(String[] args) { 3 NewPerson person = new NewPerson(); 4 person.receive(new Email2()); 5 person.receive(new Weixin()); 6 } 7 } 8 9 interface IReceiver { 10 String getMessage(); 11 } 12 13 class Email2 implements IReceiver { 14 public String getMessage() { 15 return "获得邮件消息"; 16 } 17 } 18 19 class Weixin implements IReceiver { 20 public String getMessage() { 21 return "获得微信消息"; 22 } 23 } 24 25 class NewPerson { 26 public void receive(IReceiver e){ 27 System.out.println(e.getMessage()); 28 } 29 }
3)总结
高层模块不应该依赖低层模块,二者都应该依赖其抽象
抽象不应该依赖细节,细节应该依赖抽象
中心思想是面向接口编程
相对于细节都多变性,抽象要稳定都多,以抽象为基础搭建多架构比以细节为基础多架构更稳定。抽象即接口或抽象类,细节即具体的实现类
用接口或抽象类目的是制定规范,而不设计具体操作,把展示细节交给实现类完成
4)扩展
依赖倒置的三种方法
1 public class InversionDemo3 { 2 public static void main(String[] args) { 3 // 方式一 4 Tourism tourism = new Tourism(); 5 tourism.ride(new Bus()); 6 7 // 方式二 8 Tourism tourism = new Tourism(new AirCraft()); 9 tourism.ride(); 10 // 方式三 11 Tourism tourism = new Tourism(); 12 tourism.setTraffic(new Bus()); 13 tourism.ride(); 14 } 15 } 16 17 // 方式1。接口传递 18 //旅行 19 interface ITourism { 20 public void ride(ITraffic i); 21 } 22 23 // 交通 24 interface ITraffic { 25 public void tool(); 26 } 27 28 //大巴 29 class Bus implements ITraffic { 30 public void tool() { 31 System.out.println("乘大巴旅行"); 32 } 33 } 34 35 //飞机 36 class AirCraft implements ITraffic { 37 public void tool() { 38 System.out.println("乘飞机旅行"); 39 } 40 } 41 42 // 旅行类 43 class Tourism implements ITourism { 44 public void ride(ITraffic t) { 45 t.tool(); 46 } 47 } 48 49 50 //方式2 构造 51 interface ITourism { 52 public void ride(); 53 } 54 // 交通 55 interface ITraffic { 56 public void tool(); 57 } 58 59 //大巴 60 class Bus implements ITraffic{ 61 public void tool() { 62 System.out.println("乘大巴旅行"); 63 } 64 } 65 66 //飞机 67 class AirCraft implements ITraffic{ 68 public void tool() { 69 System.out.println("乘飞机旅行"); 70 } 71 } 72 73 // 旅行类 74 class Tourism implements ITourism { 75 public ITraffic t; 76 public Tourism (ITraffic t){ 77 this.t =t; 78 } 79 public void ride() { 80 t.tool(); 81 } 82 } 83 84 // 方式三 Setter注入 85 interface ITourism { 86 public void ride(); 87 public void setTraffic(ITraffic t); 88 } 89 // 交通 90 interface ITraffic { 91 public void tool(); 92 } 93 94 //大巴 95 class Bus implements ITraffic{ 96 public void tool() { 97 System.out.println("乘大巴旅行"); 98 } 99 } 100 101 ////飞机 102 class AirCraft implements ITraffic{ 103 public void tool() { 104 System.out.println("乘飞机旅行"); 105 } 106 } 107 108 // 旅行类 109 class Tourism implements ITourism { 110 private ITraffic t; 111 public void setTraffic(ITraffic t) { 112 this.t =t; 113 } 114 public void ride() { 115 t.tool(); 116 } 117 }
4.里氏替换原则
1)概念
子类可以扩展父类功能,但不能改变父类原有功能。将一个基类对象替换成它子类,程序不会产生任何错误和异常
2)实例
1 public class RichterReplaceDemo1 { 2 public static void main(String[] args) { 3 A a = new A(); 4 System.out.println("8+1:"+a.method(8, 1)); 5 6 B b = new B(); 7 System.out.println("8+1:"+b.method(8,1)); 8 9 10 } 11 } 12 13 class A { 14 public int method(int n1,int n2){ 15 return n1+n2; 16 } 17 } 18 19 class B extends A { 20 // 不小心重写父类方法 21 public int method(int n1,int n2){ 22 return n1-n2; 23 } 24 25 public int method2(int n1,int n2) { 26 return (n1+n2)*3; 27 } 28 }
B类继承A类,调用者不小心修改父类方法。怎么改进尼?建立一个更小的基类。
1 public class RichterReplaceDemo2 { 2 public static void main(String[] args) { 3 A1 a = new A1(); 4 System.out.println("8+1:"+a.method(8, 1)); 5 6 B1 b = new B1(); 7 System.out.println("8-1:"+b.method(8,1)); 8 } 9 } 10 11 //添加一个更基础但基类 12 class Base { 13 14 } 15 16 class A1 extends Base { 17 public int method(int n1,int n2){ 18 return n1+n2; 19 } 20 } 21 22 // B类不在继承A,调用功能很明确,调用者不会改错 23 class B1 extends Base { 24 public int method(int n1,int n2){ 25 return n1-n2; 26 } 27 28 public int method2(int n1,int n2) { 29 return (n1+n2)*3; 30 } 31 }
5.开闭原则
1)概念
对扩展开发,对修改关闭。
2)实例
1 public class OpenCloseDemo1 { 2 public static void main(String[] args) { 3 BackHome backHome = new BackHome(); 4 backHome.backHome(new Walk()); 5 backHome.backHome(new Car()); 6 } 7 } 8 9 // 回家 10 class BackHome { 11 public void backHome(Back b){ 12 if(b.type ==1){ 13 walk(); 14 }else if (b.type ==2){ 15 car(); 16 } 17 } 18 19 public void walk(){ 20 System.out.println("步行回家"); 21 } 22 23 public void car(){ 24 System.out.println("开车回家"); 25 } 26 } 27 // 基类 28 class Back { 29 int type; 30 } 31 //步行 32 class Walk extends Back{ 33 Walk(){ 34 super.type=1; 35 } 36 } 37 //开车 38 class Car extends Back{ 39 Car(){ 40 super.type=2; 41 } 42 }
优点:理解很容易
缺点:难扩展,违反开闭原则
改进:基类改成抽象类 并提供抽象方法
1 public class OpenCloseDemo2 { 2 public static void main(String[] args) { 3 NewBackHome newBackHome = new NewBackHome(); 4 newBackHome.backHome(new NewWalk()); 5 newBackHome.backHome(new NewCar()); 6 newBackHome.backHome(new NewSubWay()); 7 } 8 } 9 10 // 回家 11 class NewBackHome { 12 public void backHome(NewBack b){ 13 b.BackHome(); 14 } 15 16 } 17 // 基类-》改成抽象类 18 abstract class NewBack { 19 int type; 20 public abstract void BackHome();// 抽象方法 21 } 22 //步行 23 class NewWalk extends NewBack{ 24 NewWalk(){ 25 super.type=1; 26 } 27 28 public void BackHome() { 29 System.out.println("步行回家"); 30 } 31 } 32 //开车 33 class NewCar extends NewBack{ 34 NewCar(){ 35 super.type=2; 36 } 37 38 public void BackHome() { 39 System.out.println("开车回家"); 40 } 41 } 42 43 //地铁 44 class NewSubWay extends NewBack{ 45 NewSubWay(){ 46 super.type=3; 47 } 48 49 public void BackHome() { 50 System.out.println("地铁回家"); 51 } 52 }
6.迪米特法原则(最少知道原则)
1)概念
一个类对它依赖对类知道对越少越好,我们称成员变量,方法参数,方法返回值为直接朋友,而局部变量不是直接朋友。
2)实例
1 public class LodDemo1 { 2 public static void main(String[] args) { 3 D d = new D(); 4 d.printAll(new C()); 5 } 6 } 7 8 class A { 9 private int id; 10 public int getId() { 11 return id; 12 } 13 14 public void setId(int id) { 15 this.id = id; 16 } 17 } 18 19 class B { 20 private int id; 21 public int getId() { 22 return id; 23 } 24 25 public void setId(int id) { 26 this.id = id; 27 } 28 } 29 30 class C { 31 public List<A> getAllA(){ 32 List<A> as = new ArrayList<A>(); 33 for (int i = 0; i <5 ; i++) { 34 A a = new A(); 35 a.setId(i); 36 as.add(a); 37 } 38 return as; 39 } 40 } 41 42 class D { 43 public List<B> getAllB(){ 44 List<B> bs = new ArrayList<B>(); 45 for (int i = 0; i <10 ; i++) { 46 B b = new B(); 47 b.setId(i); 48 bs.add(b); 49 } 50 return bs; 51 } 52 53 public void printAll(C c){ 54 List<A> allA = c.getAllA(); 55 for (A a: allA) { 56 System.out.println("A:"+a.getId()); 57 } 58 List<B> allB = this.getAllB(); 59 for (B b: allB) { 60 System.out.println("B:"+b.getId()); 61 } 62 } 63 }
类D的直接朋友:类B(方法返回值) 类C(方法参数)。非直接朋友:类A
违法迪米特原则,怎么改进???
1 public class LodDemo2 { 2 public static void main(String[] args) { 3 D1 d = new D1(); 4 d.printAll(new C1()); 5 } 6 } 7 8 class A1 { 9 private int id; 10 public int getId() { 11 return id; 12 } 13 14 public void setId(int id) { 15 this.id = id; 16 } 17 } 18 19 class B1 { 20 private int id; 21 public int getId() { 22 return id; 23 } 24 25 public void setId(int id) { 26 this.id = id; 27 } 28 } 29 30 class C1 { 31 public List<A1> getAllA(){ 32 List<A1> as = new ArrayList<A1>(); 33 for (int i = 0; i <5 ; i++) { 34 A1 a = new A1(); 35 a.setId(i); 36 as.add(a); 37 } 38 return as; 39 } 40 41 public void printA(){ 42 List<A1> allA = getAllA(); 43 for (A1 a: allA) { 44 System.out.println("A:"+a.getId()); 45 } 46 } 47 48 } 49 50 class D1 { 51 public List<B1> getAllB(){ 52 List<B1> bs = new ArrayList<B1>(); 53 for (int i = 0; i <10 ; i++) { 54 B1 b = new B1(); 55 b.setId(i); 56 bs.add(b); 57 } 58 return bs; 59 } 60 61 public void printAll(C1 c){ 62 c.printA(); 63 List<B1> allB = this.getAllB(); 64 for (B1 b: allB) { 65 System.out.println("B:"+b.getId()); 66 } 67 } 68 }
7.合成复用原则
1)概念
尽量使用合成/聚合方式,而不是使用继承
2)实例
1 public class CompositeDemo1 { 2 public static void main(String[] args) { 3 B b = new B(); 4 b.method3(); 5 } 6 } 7 8 class A { 9 public void method1(){ 10 System.out.println("A:method1"); 11 } 12 13 public void method2(){ 14 System.out.println("A:method2"); 15 } 16 } 17 18 class B extends A { 19 public void method3(){ 20 method1(); 21 } 22 }
为了使用类A的方法,类B去继承类A,增强了A和B的耦合
1 public class CompositeDemo2 { 2 public static void main(String[] args) { 3 B1 b1 = new B1(); 4 // 解决方法1 依赖 5 b1.method3(new A1()); 6 7 //解决方法2 聚合 8 b1.setA(new A1()); 9 b1.method4(); 10 11 //解决方法2 组合 12 b1.method5(); 13 14 } 15 } 16 17 class A1 { 18 public void method1(){ 19 System.out.println("A:method1"); 20 } 21 22 public void method2(){ 23 System.out.println("A:method2"); 24 } 25 } 26 27 class B1 { 28 // 解决方法一 29 public void method3(A1 a){ 30 a.method1(); 31 } 32 33 // 解决方法二 34 private A1 a; 35 36 public A1 getA() { 37 return a; 38 } 39 40 public void setA(A1 a) { 41 this.a = a; 42 } 43 44 public void method4(){ 45 a.method1(); 46 } 47 48 // 解决方法3 49 private A1 a1 = new A1(); 50 public void method5(){ 51 a1.method1(); 52 } 53 }
总结:
1.找出应用中可能变化之处,把它独立出来。
2.针对接口编程,而不是实现编程
3.为了交互设计之间松耦合设计
三.二十三种设计模式
1.创建型模式
1)单例模式
1⃣️饿汉式 静态常量
优点:类加载就实例化,避免线程同步
缺点:如果类没被使用,则导致内存浪费
1 class Sigleton01 { 2 // 私有构造器 防止被new对象 3 private Sigleton01(){} 4 5 // 内部创建对象 6 private final static Sigleton01 instance =new Sigleton01(); 7 8 // 对外暴露静态公共方法 9 public static Sigleton01 getInstance(){ 10 return instance; 11 } 12 }
2⃣️饿汉式 静态代码块
优缺点同上
1 class Singleton02{ 2 private Singleton02(){} 3 4 private static Singleton02 instance; 5 6 // 静态代码块 创建实例 7 static { 8 instance=new Singleton02(); 9 } 10 11 public static Singleton02 getInstance(){ 12 return instance; 13 } 14 }
3⃣️懒汉式 线程不安全
优点:调用时候才实例化,节省内存
缺点:多线程不安全
1 class Singleton03{ 2 private Singleton03(){} 3 4 private static Singleton03 instance; 5 6 public static Singleton03 getInstance(){ 7 if(instance ==null){ 8 instance = new Singleton03(); 9 } 10 return instance; 11 } 12 }
4⃣️懒汉式 synchranized
优点:线程安全
缺点:同步效率低
1 class Singleton04{ 2 private Singleton04(){} 3 4 private static Singleton04 instance; 5 6 public static synchronized Singleton04 getInstance(){ 7 if(instance ==null){ 8 instance = new Singleton04(); 9 } 10 return instance; 11 } 12 } 13 or 14 class Singleton04{ 15 private Singleton04(){} 16 17 private static Singleton04 instance; 18 19 public static Singleton04 getInstance(){ 20 if(instance ==null){ 21 synchronized(Singleton04.Class){ 22 instance = new Singleton04(); 23 } 24 25 } 26 return instance; 27 } 28 }
5⃣️懒汉式 双端检锁检测(推荐)
优点:线程安全,同步效率高
1 class Singleton05{ 2 private Singleton05(){} 3 4 private volatile static Singleton05 instance; 5 6 public static Singleton05 getInstance(){ 7 if(instance ==null){ 8 synchronized (Singleton05.class){ 9 if(instance ==null){ 10 instance = new Singleton05(); 11 } 12 } 13 } 14 return instance; 15 } 16 }
6⃣️静态内部类(推荐)
优点:线程安全,同步效率高
原理:类的静态属性只有在第一次加载时初始化,jvm保证了线程的安全。静态内部类被装载时不会实例化,调用时候才会
1 class Singleton06{ 2 private Singleton06(){} 3 4 private static class Singleton06Instance{ 5 private static final Singleton06 INSTANCE=new Singleton06(); 6 } 7 8 public static Singleton06 getInstance(){ 9 return Singleton06Instance.INSTANCE; 10 } 11 }
7⃣️枚举(推荐)
优点:线程安全,防反序列化重写创建对象
1 enum Singleton07{ 2 INSTANCE; 3 }
总结:
针对一些需要频繁创建和销毁的对象使用如工具类,数据源等。
2)抽象工厂模式
3)原型模式
4)建造者模式
5)工厂模式
本实例demo通过设计一个计算器来讲述工厂模式的实现流程。
2.结构型模式
1)适配器模式
2)桥接模式
3)装饰者模式
本实例demo通过设计人物服饰装配来讲述装饰者模式的实现流程
4)组合模式
5)外观模式
6)享元模式
7)代理模式
3.行为型模式
1)模板方法模式
2)命令模式
3)访问者模式
4)迭代器模式
5)观察者模式
6)中介者模式
7)备忘录模式
8)解释器模式
9)状态模式
10)策略模式
本实例demo通过设计一个超市的收款方式来讲述策略模式的实现流程。
11)责任链模式
持续更新...