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{
@Override
public void opration1() {
System.out.println("C opration1");
}
@Override
public void opration2() {
System.out.println("C opration2");
}
@Override
public void opration3() {
System.out.println("C opration3");
}
@Override
public void opration4() {
System.out.println("C opration4");
}
@Override
public void opration5() {
System.out.println("C opration5");
}
}
class D implements Interface1{
@Override
public void opration1() {
System.out.println("D opration1");
}
@Override
public void opration2() {
System.out.println("D opration2");
}
@Override
public void opration3() {
System.out.println("D opration3");
}
@Override
public void opration4() {
System.out.println("D opration4");
}
@Override
public 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{
@Override
public void opration1() {
System.out.println("C opration1");
}
@Override
public void opration2() {
System.out.println("C opration2");
}
@Override
public void opration5() {
System.out.println("C opration5");
}
}
class D1 implements Interface21, Interface23{
@Override
public void opration1() {
System.out.println("D opration1");
}
@Override
public void opration3() {
System.out.println("D opration3");
}
@Override
public 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;
}
@Override
void pay() {
System.out.println("京东支付");
}
}
class WechatPay extends Pay {
public WechatPay() {
super.type = 2;
}
@Override
void pay() {
System.out.println("微信支付");
}
}
class AliPay extends Pay {
public AliPay() {
super.type = 3;
}
@Override
void pay() {
System.out.println("支付宝支付");
}
}
使用方法二,使用类,无需改动,以后扩展其他支付,只需要增加对应的支付类
6. 迪米特法则
只与直接朋友通信,
直接朋友的定义,成员变量、方法参数,方法返回值,局部变量不是直接朋友
不是直接朋友的类,是陌生的类
判断思路
在类里面找非直接朋友,如果找到,就将非直接朋友迁移到其他类中
7. 合成复用原则
尽量使用聚合/合成方式,而不是使用集成
简单讲类A 有方法f1,类B想使用f1,简单的办法,B继承A,但是继承就提高了AB之间的耦合,A可以作为参数传递给B使用A方法的方法,A也可以作为B的成员变量通过set方法传递过去,也可以直接在B中通过new来进行组合
8.设计模式的核心思想
把需要变化的独立出来,不要和那些不需要变化的代码混合起来
针对接口编程而不是针对实现编程
为了交互对象之间松耦合设计而努力