一、《重学设计模式》-设计模式简介
1. 设计模式分类
序号 | 模式&描述 | 包括 |
---|---|---|
1 | 创建型模式 | 1.工厂 2.抽象工厂 3.建造者 4.原型 5.单例 |
2 | 结构型模式 | 1.适配器 2.桥接 3.过滤器 4.组合 5.装饰器 6.外观 7.享元 8.代理 |
3 | 行为模式 | 1.责任链 2.命令 3.解释器 4.迭代器 5.中介者 6.备忘录 7.观察者 8.状态 9.空对象 10.策略 11.模板 12.访问者 |
2. 设计模式关系图
3. 设计模式的七大原则
- 单一职责( 一个类和方法只做一件事 )
- 里⽒替换( 多态,子类可扩展父类 )
- 依赖倒置( 细节依赖抽象,下层依赖上层 )
- 接口隔离( 建⽴单一接口 )
- 迪米特原则( 最少知道,降低耦合 )
- 开闭原则( 抽象架构,扩展实现 )
- 合成复用原则
1. 单一职责
方案一
public class Demo1 {public static void main(String[] args) {SingleResponsibility singleResponsibility = new SingleResponsibility();singleResponsibility.run("小鸡");singleResponsibility.run("小鱼");singleResponsibility.run("小鸟");}static class SingleResponsibility {public void run(String name) {System.out.println(name + " 在跑");}}
}
=========================
小鸡 在跑
小鱼 在跑
小鸟 在跑
以上代码run方法违反了单一职责原则,解决方案也很简单,将SingleResponsibility 分成三个,陆地、天空、水中
方案二
public class Demo2 {public static void main(String[] args) {SingleResponsibilityLand singleResponsibility1 = new SingleResponsibilityLand();SingleResponsibilitySky singleResponsibility2 = new SingleResponsibilitySky();SingleResponsibilityWater singleResponsibility3 = new SingleResponsibilityWater();singleResponsibility1.run("小鸡");singleResponsibility3.run("小鱼");singleResponsibility2.run("小鸟");}static class SingleResponsibilityLand {public void run(String name) {System.out.println(name + " 在跑");}}static class SingleResponsibilitySky {public void run(String name) {System.out.println(name + " 在飞");}}static class SingleResponsibilityWater {public void run(String name) {System.out.println(name + " 在游");}}}
===================
小鸡 在跑
小鱼 在游
小鸟 在飞
成功解决了方案一的缺陷,遵守了单一职责,但是这样做改动很大,分解了多个类,修改了main方法
方案三
public static void main(String[] args) {SingleResponsibility singleResponsibility = new SingleResponsibility();singleResponsibility.runLand("小鸡");singleResponsibility.runWater("小鱼");singleResponsibility.runSky("小鸟");}static class SingleResponsibility {public void runLand(String name) {System.out.println(name + " 在跑");}public void runWater(String name) {System.out.println(name + " 在游");}public void runSky(String name) {System.out.println(name + " 在飞");}}
这种方法没有对最初的类进行大的修改,只是增加了方法,虽然没有在类级别遵守单一职责,但是在方法上面满足了单一职责
注意事项
- 降低类的复杂度,一个类只负责一项职责
- 提高类的可读性,可维护性
- 降低变更引起的风险
- 通常情况下,应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则
2. 接口隔离原则
方案一
public class Demo2_1 {public static void main(String[] args) {A a = new A();a.opration1(new C());a.opration2(new C());a.opration5(new C());B b = new B();b.opration1(new D());b.opration3(new D());b.opration4(new D());}
}interface Interface1{void opration1();void opration2();void opration3();void opration4();void opration5();
}class A {public void opration1(Interface1 interface1) {interface1.opration1();}public void opration2(Interface1 interface1) {interface1.opration2();}public void opration5(Interface1 interface1) {interface1.opration5();}
}class B {public void opration1(Interface1 interface1) {interface1.opration1();}public void opration3(Interface1 interface1) {interface1.opration3();}public void opration4(Interface1 interface1) {interface1.opration4();}
}class C implements Interface1{@Overridepublic void opration1() {System.out.println("C opration1");}@Overridepublic void opration2() {System.out.println("C opration2");}@Overridepublic void opration3() {System.out.println("C opration3");}@Overridepublic void opration4() {System.out.println("C opration4");}@Overridepublic void opration5() {System.out.println("C opration5");}
}
class D implements Interface1{@Overridepublic void opration1() {System.out.println("D opration1");}@Overridepublic void opration2() {System.out.println("D opration2");}@Overridepublic void opration3() {System.out.println("D opration3");}@Overridepublic void opration4() {System.out.println("D opration4");}@Overridepublic void opration5() {System.out.println("D opration5");}
}
类A通过接口 Interface1 依赖类C,类B通过接口 Interface1 依赖类D,如果接口 Interface1对于类C和类D来说不是最小接口,那么类C和类D必须去实现他们不需要的方法, 改进办法,将interface1拆分成三个接口,让他们分别实现不同的接口
方案二
public class Demo2_2 {public static void main(String[] args) {A1 a = new A1();a.opration1(new C1());a.opration2(new C1());a.opration5(new C1());B1 b = new B1();b.opration1(new D1());b.opration3(new D1());b.opration4(new D1());}
}interface Interface21{void opration1();
}
interface Interface22{void opration2();void opration5();
}
interface Interface23{void opration3();void opration4();}class A1 {public void opration1(C1 interface1) {interface1.opration1();}public void opration2(C1 interface1) {interface1.opration2();}public void opration5(C1 interface1) {interface1.opration5();}
}class B1 {public void opration1(D1 interface1) {interface1.opration1();}public void opration3(D1 interface1) {interface1.opration3();}public void opration4(D1 interface1) {interface1.opration4();}
}class C1 implements Interface21, Interface22{@Overridepublic void opration1() {System.out.println("C opration1");}@Overridepublic void opration2() {System.out.println("C opration2");}@Overridepublic void opration5() {System.out.println("C opration5");}
}
class D1 implements Interface21, Interface23{@Overridepublic void opration1() {System.out.println("D opration1");}@Overridepublic void opration3() {System.out.println("D opration3");}@Overridepublic void opration4() {System.out.println("D opration4");}
}
方案二将接口全部拆分,依赖类只需要实现自己需要的接口
3. 依赖倒转(接口或抽象类)
方案一
public class Demo3_1 {public static void main(String[] args) {Somebody somebody = new Somebody();somebody.receive(new Email());}
}class Email{public void say(){System.out.println("邮件");}
}class Somebody{// 传参固定,扩展其他内容有困难public void receive(Email email){email.say();}
}
Somebody如果要实现接收微信,短信消息扩展困难
方案二
public class Demo3_2 {public static void main(String[] args) {XiaoQiang xiaoQiang = new XiaoQiang();xiaoQiang.receive(new Wechat());xiaoQiang.receive(new SMS());}
}interface Message{void say();
}class Wechat implements Message{public void say(){System.out.println("微信");}
}
class SMS implements Message{public void say(){System.out.println("短信");}
}class XiaoQiang{public void receive(Message message){message.say();}
}
方案二提供公共接口,参数用接口形式传递,可以接收不同类型的消息,同时好扩展
依赖关系传递
- 接口传递
- 构造器传递
- setter方法传递
4. 里氏替换原则(继承)
子类尽量不要重写父类的方法,继承实际上是让两个类的耦合性增强,适当的使用聚合、组合、依赖来解决问题
方案一
public class Demo4_1 {public static void main(String[] args) {A4 a = new A4();System.out.println("1+1="+a.fun1(1,1));System.out.println("1+3="+a.fun1(1,3));B4 b = new B4();System.out.println("1+1="+b.fun1(1,1));System.out.println("1+1+9="+b.fun2(1,1));}}
class A4 {public int fun1(int a , int b ){return a+b;}
}class B4 extends A4{//重写父类方法public int fun1(int a , int b ){return a-b;}public int fun2(int a , int b ){return fun1(a,b) + 9;}
}
方案一存在问题,B继承A 但是修改了A方法的逻辑,导致后面使用相同方法但是意义不一样,和预想结果发生偏差
方案二
public class Demo4_2 {public static void main(String[] args) {A42 a = new A42();System.out.println("1+1=" + a.fun1(1, 1));System.out.println("1+3=" + a.fun1(1, 3));B42 b = new B42();System.out.println("1+1=" + b.fun1(1, 1));System.out.println("1+1+9=" + b.fun2(1, 1));}}class Base4 {}class A42 extends Base4 {public int fun1(int a, int b) {return a + b;}
}class B42 extends Base4 {//通过组合方式解决方法名称相同,内容不同A42 a42 = new A42();//重写父类方法public int fun1(int a, int b) {return a - b;}public int fun2(int a, int b) {return fun1(a, b) + 9;}public int fun3(int a, int b) {return a42.fun1(a, b);}
}
提取公共类,实现各自继承,解决单一继承修改方法导致方法意义变更的问题,如果B想使用A,使用组合方式实现
5. 开闭原则
扩展开放(提供方),修改关闭(使用方),抽象构建框架,实现扩展细节
软件变化尽量通过扩展,而不是通过修改
方案一
public class Demo5_1 {public static void main(String[] args) {Bank bank = new Bank();bank.pay(new JDPay());bank.pay(new WechatPay());bank.pay(new AliPay());}
}class Bank {// 使用方public void pay(Pay pay) {if (pay.type == 1) {System.out.println("京东支付");} else if (pay.type == 2) {System.out.println("微信支付");} else if (pay.type == 3) {System.out.println("支付宝支付");}}}class Pay {int type;
}class JDPay extends Pay {public JDPay() {super.type = 1;}
}class WechatPay extends Pay {public WechatPay() {super.type = 2;}
}class AliPay extends Pay {public AliPay() {super.type = 3;}
}
方案一,当我们想要使用银联支付的时候,需要添加银联支付的类,需要修改实用类,都要修改,不符合开闭原则
方案二
public class Demo5_2 {public static void main(String[] args) {Bank bank = new Bank();bank.pay(new JDPay());bank.pay(new WechatPay());bank.pay(new AliPay());}
}class Bank {// 使用方public void pay(Pay pay) {pay.pay();}}abstract class Pay {int type;abstract void pay();
}class JDPay extends Pay {public JDPay() {super.type = 1;}@Overridevoid pay() {System.out.println("京东支付");}
}class WechatPay extends Pay {public WechatPay() {super.type = 2;}@Overridevoid pay() {System.out.println("微信支付");}
}class AliPay extends Pay {public AliPay() {super.type = 3;}@Overridevoid pay() {System.out.println("支付宝支付");}
}
使用方法二,使用类,无需改动,以后扩展其他支付,只需要增加对应的支付类
6. 迪米特法则
只与直接朋友通信,
直接朋友的定义,成员变量、方法参数,方法返回值,局部变量不是直接朋友
不是直接朋友的类,是陌生的类
判断思路
在类里面找非直接朋友,如果找到,就将非直接朋友迁移到其他类中
7. 合成复用原则
尽量使用聚合/合成方式,而不是使用集成
简单讲类A 有方法f1,类B想使用f1,简单的办法,B继承A,但是继承就提高了AB之间的耦合,A可以作为参数传递给B使用A方法的方法,A也可以作为B的成员变量通过set方法传递过去,也可以直接在B中通过new来进行组合
8.设计模式的核心思想
-
把需要变化的独立出来,不要和那些不需要变化的代码混合起来
-
针对接口编程而不是针对实现编程
-
为了交互对象之间松耦合设计而努力