设计模式、系统设计 record part04

结构型模式

在这里插入图片描述
结构型模式分为:
1.类结构型模式
2.对象结构型模式
3。类结构型,使用继承机制,耦合度高,不灵活
4.对象结构型,使用组合、聚合关系,耦合低,灵活


代理模式

在这里插入图片描述
1.代理就是中介
2.静态代理,在编译期生成,就是写代码的时候就存在了
3.动态代理,在Java运行时生成,执行代码时才有,
4.动态代理分为,JDK、CGLib 两种
CGLib(Code Generation Library) 代码生成库
在这里插入图片描述 1.真实主题 Real Subject,是最终要被引用的对象,就是给它做代理,> 代理类 Proxy ,在其内部引用真实主题 Real Subject,对 Real Subject 能看、能写
在这里插入图片描述

  1. 火车站、是真实主题 Real Subject,
    2.代理点,是代理,引用了火车站的功能,方法sell()卖票,
    3.代理点、火车站都要去实现接口SellTichets
    聚合关系用菱形表示,不是箭头,而且是空心菱形
    实验相关类,如下:
    在这里插入图片描述
    抽象主题类,接口 SellTickets,代码如下:
    在这里插入图片描述
    真实主题类 TrainStation,代码如下:
    在这里插入图片描述
    代理类 ProxyPoint ,代码如下:
    在这里插入图片描述
    可见,代理类 ProxyPoint 和 真实主题类 TrainStation,都去实现了 抽象主题类,接口 SellTickets,
    测试类 Client ,代码如下:
    在这里插入图片描述
    通过操作代理来间接的使用真实主题中的方法,
    也就是说,真实主题中的方法只允许代理来调用,所以要想使用真实主题中的方法,就必须通过代理来达成。
    运行结果,如下:
    在这里插入图片描述

JDK动态代理
在这里插入图片描述
.newProxyInstance()获取代理对象
相关实验包含的类,如下:
在这里插入图片描述
抽象主题,接口 SellTickets,代码如下:
在这里插入图片描述
真实主题 TrainStation,代码如下:
在这里插入图片描述
代理工厂 ProxyFactory,代码如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
October2024the06thSunday
1.
在这里插入图片描述
在这里插入图片描述
2.
在这里插入图片描述
在这里插入图片描述
3.
在这里插入图片描述
在这里插入图片描述
通过newProxyInstance()方法可以拿到类型是SellTickets接口的代理对象proxyObject代理对象,但newProxyInstance()方法里要传入参数 如上面描述的1、2、3,
代码如下:
在这里插入图片描述
测试类 Client,代码如下:
在这里插入图片描述
测试结果,如下:
在这里插入图片描述
通过反编译工具查看动态代理对象是如何实现的,底层代码,如下
在这里插入图片描述
使用反射机制拿到动态代理类的对象,其中使用到类的路径、方法,如下:
在这里插入图片描述

CGLIB 动态代理
在这里插入图片描述
1.JDK 动态代理,要求,必须定义,接口,
然后,才是对接口进行代理,
2,CGLIB 动态代理,为没有实现接口的类,
提供代理,对 JDK 动态代理进行了补充,
3.CGLIB 动态代理,是第三方包,需要引入jar包

两种动态代理方式的对比,如下:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
CGLIB 动态代理实验涉及的类,如下:
在这里插入图片描述
注意,这里已经不需要接口了,
其中的被代理类(目标类) TrainStation,代码如下:
在这里插入图片描述
代理类 ProxyFactory ,代码如下:
在这里插入图片描述
在这里插入图片描述
.create()方法会自动去调用 intercept()方法,
测试类 Clinet ,代码如下:
在这里插入图片描述
运行结果,如下:
在这里插入图片描述


适配器模式

在这里插入图片描述
在这里插入图片描述
分为 类适配器模式、对象适配器模式,
在这里插入图片描述
SDAdapterTF 通过继承关系,就获得了 TFCardImpl 中的关于TF相关的能力,
继承关系,使用实线+空心三角形表示,

类适配器实验,相关类和接口,如下:
在这里插入图片描述
有读取SD的需求,computer 类 代码如下,
在这里插入图片描述
适配者(Adaptee)SDCard ,它是接口,代码如下:
在这里插入图片描述
适配者(Adaptee)SDCard 的实现类 SDCardImpl ,如下:
在这里插入图片描述
目标接口( Target )TFCard 接口代码如下:
在这里插入图片描述
目标接口 TFCard 的实现类 TFCard Impl ,代码如下:
在这里插入图片描述
适配器 (Adapter)SDAdapterTF ,代码如下:
在这里插入图片描述
SDAdapterTF 既实现适配者 SDCard 接口, 又继承了 目标接口 TFCard 的实现类 TFCard Impl,所以它同时拥有两个接口的所有方法,这样就达到了适配的目的,即让使用者 computer 同时使用 SD、TF 2种卡。
注意,上图中适配器SDAdapterTF 使用的是适配者 SDCard 接口中的方法 readSD(),但在这个方法里却能调用目标接口 TFCard中的方法 readTF(),
原因就是,SDAdapterTF 既实现适配者 SDCard 接口, 又继承了 目标接口 TFCard 的实现类 TFCard Impl,
这里还有一个很巧妙的事情是,SDAdapterTF 实现适配者 SDCard 接口,它就可以被当作参数被传参进 使用者 computer 里,这样,
使用者computer 如果想用SD卡,就直接给自己传 适配者(Adaptee)SDCard 的实现类 SDCardImpl的对象,
在这里插入图片描述
如果要用TF卡,就给自己传适配器 (Adapter)SDAdapterTF的对象。
在这里插入图片描述
测试类 Client 代码如下:
在这里插入图片描述
测试结果,如下:
在这里插入图片描述

对象适配器实验,相关概念,如下:
在这里插入图片描述
空心菱形表示聚合关系,就是包含的关系,空心菱形指向适配器,说明 适配器 SDAdapterTF 包含 接口 TFCard,
两者的区别在于,类适配器,去继承TF的实现类,如下
在这里插入图片描述
对象适配器,与TF接口是聚合关系,如下
在这里插入图片描述
1.适配器多了一个属性,属性类型是TF接口,
2.适配器和TF接口之间是聚合关系,适配器 SDAdapterTF 包含 接口 TFCard
对象适配器实验,类和接口,如下
在这里插入图片描述
适配者(Adaptee),接口 SDCard ,代码如下:
在这里插入图片描述
SD卡的接口 SDCard 的接口实现类 SDCardImpl ,如下:
在这里插入图片描述
目标接口 TFCard ,代码如下:
在这里插入图片描述
目标接口 TFCard 的实现类 TFCardImpl ,代码如下
和类适配器中的对应代码大差不差
对象适配器 SDAdapterTF ,代码如下
在这里插入图片描述

在对象适配器 SDAdapterTF 类中组合一个目标接口 TFCard ,作为 SDAdapterTF 的属性,如下
在这里插入图片描述
接下来通过对象适配器 SDAdapterTF 类的构造方法,就能拿到
目标接口 TFCard 的对象,有了TFCard 的对象就能用TFCard 的对象中的方法了,同时,对象适配器 SDAdapterTF 本身又实现了适配者(Adaptee)接口 SDCard,所以,又可以使用接口 SDCard中的方法,
使用者 computer 类,代码如下
在这里插入图片描述
对应的测试类 Clinet,代码如下:
在这里插入图片描述
运行结果如下
在这里插入图片描述


装饰者模式

在这里插入图片描述
UML类图中空心三角形,表示继承,
上图中所有的类之间,存在的关系,都是继承关系
上图中的UML类图是一个存在问题(会产生过多的子类)的原始做法,待用装饰器模式进行改进,
在这里插入图片描述
用装饰器模式进行改进,UML类图,如下
在这里插入图片描述
空心菱形,是聚合关系,表示包含,上图中空心菱形的位置是错误的,它应该在Garnish类这边,因为空心菱形指向整体,而FastFood是被包含在Garnish中的,如下
在这里插入图片描述
所以,Garnish 是整体,它包含 FastFood,UML类图应该如下
在这里插入图片描述
装饰者模式实验中包含的类如下,
在这里插入图片描述
抽象构件(component)FastFood ,代码如下
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
一个具体构件(concrete component)FriedRice,代码如下,
在这里插入图片描述
另一个具体构件(concrete component)FriedNoodles,代码如下,
在这里插入图片描述
抽象装饰(decorator)Garnish,代码如下
在这里插入图片描述
在这里插入图片描述
这里,可以看到上面代码中Garnish是FastFood的子类,所以有继承关系,
那么之前的图应该再改一下,如下,
在这里插入图片描述
两个方向上都应该有关系,
向左是子类Garnish继承父类FastFood,
向右是Garnish 聚合了 FastFood
Garnish 继承并聚合 FastFood
在这里插入图片描述
一个具体装饰(concrete decorator)Egg,代码如下:
在这里插入图片描述
这里有个难点 super(fastFood,price:1,desc:‘鸡蛋’),需要理解,Egg继承的Garnish,而Garnish就是装饰品,所以,Egg也是装饰品,
Egg这个构造方法产生的就是Egg的对象,所以,super(fastFoood,price:1,desc:‘鸡蛋’)这个对象就是Egg,至于Egg里面的三个参数中的 fastFood 就是 等待装饰品 Egg去修饰的 炒饭、炒面,
那么问题就来了,装饰品Egg里面除了自己的对象 Egg 还有个要被它所修饰的对象(炒米、炒面)
所以,装饰品Egg里的方法 cost()里面参与计算的有两个对象,
一个是 getPrice(),这个方法没有被其他东西所修饰,所以是Egg自己的方法,但在装饰品Egg里没有,所以需要到其父类也是个装饰品 Garnish 里找,发现也没有,所以继续向上,到Egg的爷爷(Garnish的父类)FastFood里去找,果然有, getPrice()返回的是 price,既然是装饰品Egg自己方法自然返回的就是Egg自己的price,而这个price在创建 Egg对象的时候,即 super(fastFood,price:1,desc:‘鸡蛋’)的时候就给了,因此,getPrice()得到的是对象Egg的price ,1
另一个参与计算的是,getFastFood().cost(),
因为getFastFood()前面也没有其他修饰词,所以它也是对象Egg的方法,但Egg里并没有这个方法,因此要到其父类Garnish里去找,能找到,这个方法返回的是 fastfood 对象,而这个 fastfood 对象,在创建Egg的时候也给了,即 super(fastFood,price:1,desc:‘鸡蛋’),也就是被装饰品Egg所修饰的炒饭对象、炒面对象,所以,getFastFood().cost()的cost()方法就是炒米对象、炒面对象的方法,得去炒米类、炒面类里去找,炒米类、炒面类里cost()方法,返回的是炒米类、炒面类的 getPrice()方法,也就是炒米类、炒面类的price,
特别提醒,
Egg对象的 cost()方法代码是:return getPrice()+getFastFood().cost(),
fastfood对象的cost()方法代码是:return getPrice()
Egg对象的 getPrice() 方法,在它爷爷 FastFood 类里,
fastfood对象 getPrice() 方法,在它爸爸 FastFood 类里,
Egg对象的price,在 new 鸡蛋的时候,即 super(fastfood ,price:1,desc:‘鸡蛋’)给出,
fastfood对象的price,在new 炒面、new 炒饭的时候,
即 super(price:12,desc:‘炒面’)、
super(price:10,desc:‘炒饭’)给出,
super关键字代表的就是“当前对象”的那部分父类型特征。
把 fastfood 对象作为Egg对象的属性,即 fastfood 对象当作它(Egg)的实例变量。
在这里插入图片描述> 一个具体装饰(concrete decorator)Bacon,代码如下:> 在这里插入图片描述
装饰者模式测试类 client ,代码如下:
在这里插入图片描述
运行结果如下:
在这里插入图片描述
注意,下面图片中的food,就是一个炒饭的对象,
在这里插入图片描述
而下面这个图片中有两个food,
在这里插入图片描述
括号里的food 是炒饭对象,而等号左边的food 是加了蛋的炒饭,即被蛋修饰的炒饭这个整体作为一个对象,自然它的价格price和描述就和单纯的炒饭不一样了,
再看下图,
在这里插入图片描述
红线上等号左边的food是第一次加蛋后的炒饭,这个对象,
被当成参数(也就是红圈里的food对象,)送给一个新new出来的Egg对象(红框里的food),
红框里的这个新的food对象,是加了两回蛋的炒饭,即被装饰器Egg修饰了2次,因此,它的price和desc都和单纯的没被修饰器修饰过的炒饭也不一样,和加了一个蛋的炒饭的price和desc也不一样,
最后,加了2个蛋的炒饭又被培根修饰了,如下图
在这里插入图片描述
所以最后得到的等号左边的food 是一个有两个鸡蛋和一个培根的炒饭,可以这么一直套下去,想用什么修饰器都可以一直往上加,这样通过Egg、Baken这样的修饰器给普通的FriedRice增加了新的功能,而且新功能可以一直往上加,得到多样化的结果,就像穿衣服一样,一件一件的套就完事了,

在这里插入图片描述
抽象构件(Componet):FastFood
抽象装饰(Decorator) :Garnish
具体构件(Concrete Component):FriedRice、FriedNoodles
具体装饰(Concrete Decorator) :Egg、Bacon
October2024the08thTuesday
在这里插入图片描述
上图说继承是静态的,指的是,父类一旦写好后,子类继承到的责任就都是固定好的了,这时就是固定的,静态的。
假如有需求要给某个子类增加责任,要么去修改父类让这个子类去继承(但是这样的话其他的子类也会受影响),要么修改这个子类让它自己往上加东西,然而不论怎样,都需要修改父子中的某一个,
而装饰者就很灵活了,需要啥就套啥,被装饰者需要啥,就把做好的带着啥的装饰器往被装饰者身上套就行了,这样就不需要去修改被装饰者以及被装饰者的父类了,而且是随便套,想怎么套就怎么套,这就动起来了,就好比身体是爸妈给的,缺气质就穿名牌,缺自信就戴金银,缺心眼就打唇钉……是动态的,


桥接模式

在这里插入图片描述
在这里插入图片描述
继承 change to 组合
在这里插入图片描述
桥接模式实验设计的类和接口,如下
在这里插入图片描述
一个具体实现化角色(concrete Implementor)AviFile ,代码如下:
在这里插入图片描述

一个实现化角色(Implementor) VideoFile接口,代码如下:
在这里插入图片描述
VideoFile接口,它就是一个桥梁,实现化角色用来连接具体实现化角色 and 扩展抽象化角色,
让扩展抽象化角色可以调用具体实现化角色中的业务方法
另一个具体实现化角色(concrete Implementor)RmvbFile ,代码如下:
在这里插入图片描述

一个抽象化角色(Abstraction),父类 OperatingSystem,代码如下:
在这里插入图片描述

一个扩展抽象化角色(Refined Abstraction),子类 Windows,代码如下:
在这里插入图片描述
另一个扩展抽象化角色(Refined Abstraction),子类 Mac,代码如下:
在这里插入图片描述
测试类 Client,代码如下:
在这里插入图片描述
测试结果,如下:
在这里插入图片描述
在这里插入图片描述


外观模式

在这里插入图片描述
在这里插入图片描述
迪米特法则(Law of Demeter )又叫做最少知识原则,也就是说,一个对象应当尽可能少的去了解(依赖)其他对象。尽可能不和陌生人说话。英文简写为: LoD。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

扩展阅读:

1、门面模式:
为系统中的一组接口(多个子系统),向外提供一个统一的访问的入口,酒店服务的例子(客房、餐厅、洗衣)
银行柜台办理业务可以理解为门面模式的一种,客户通过银行柜台办理业务,无需了解银行内部复杂的业务逻辑处理,柜台是把银行能提供的业务展现给客户,银行没有提供的服务,柜台是无法办理。

1dvdplayer;
2soundsystem;
3screen;
4projector,
honetheaterFacade,1dvdplayer、2soundsystem、3screen、4projector 作为honetheaterFacade 的4个属性
通过honetheaterFacade 来统一调用 1dvdplayer、2soundsystem、3screen、4projector 的方法,
用户通过使用统一入口中的一个方法就可以同时调用多个子系统,而这个统一入口中的这个方法就提供了让统一入口去同时操作多个子系统的能力,因此无需用户再去分别与每个子系统都打一遍交道了,
大大简化了用户的操作,
适用于,简化复杂系统的使用,通过统一接口来操作复杂系统,
使得系统更易操作和维护

2、代理模式:
为了代理,房屋中介的例子,
代理模式比门面模式更高级
客户访问不到被代理对象,因此代理为客户(其他对象)提供一种访问、使用被代理对象的能力。这样客户可以通过代理对象间接的访问、使用被代理对象,

 interface door;
 labdoor implements door;
 securedoor implements door,labdoor 作为 securedoor 的一个属性
 new securedoor (new labdoor ());
通过使用代理 securedoor  ,来间接的访问操作 labdoor 
适用于,在访问对象时,需要做额外的操作,如权限控制
代理模式可以帮助你在不修改被代理对象的现有代码的情况下,实现对被代理对象的控制,
因为是通过代理来访问被代理对象的,所以能不能访问被代理对象都是代理说了算

3、适配器模式:
为了兼容,让两个不兼容的类可以协同一起工作,插座转换的例子、或翻译的例子
适配器模式比代理模式更复杂,
适配器模式(Adapter Pattern)
保持原有接口、功能和实现的同时,通过在适配器中包装一个不兼容的对象,来与另一个类兼容,
即,让现有类与其他类协同工作,而无需改变它们的源代码

interface mediaPlayer,有1个方法 play();
interface advancedMediaPlayer ,有2个方法 playVlc()、playAvi();
vlcPlayer implements advancedMediaPlayer ,有方法 playVlc();
aviPlayer implements advancedMediaPlayer,有方法 playAvi();
mediaAdapter implements mediaPlayer,
有两个属性 vlcMediaPlayer=new vlcPlayer() 、aviMediaPlayer=new aviPlayer();
videoPlayer implement mediaPlayer,获得一个适配器 mediaAdapter 
new mediaAdapter();
这样,videoPlayer 既可以使用原有接口mediaPlayer 的方法,又可以通过适配器 mediaAdapter 去调用接口 advancedMediaPlayer 的方法,从而达到 让原有接口 mediaPlayer  能支持 接口 advancedMediaPlayer,两者协同工作,
适配器 mediaAdapter 让原有接口 mediaPlayer 与接口 advancedMediaPlayer  ,协同工作,
注意,原有接口 mediaPlayer 是不支持 接口 advancedMediaPlayer 的,通过适配器 mediaAdapter将请求转发给接口 advancedMediaPlayer,让接口 mediaPlayer 获得了接口 advancedMediaPlayer 的能力,使得 原有接口 mediaPlayer  能支持 接口 advancedMediaPlayer 了。
适用于,现有类不支持其他类的时候,使用适配器让现有类能支持其他类,达到兼容的目的,

4、装饰器模式:
为了套娃,咖啡加奶的例子
装饰器模式(Decorator Pattern)
为对象动态地添加新的行为或责任
不改变对象代码的情况下为对象动态地添加新的职责或行为
咖啡加奶,不会改变咖啡的基本结构,但是会给咖啡增添柔和奶香的风味,

interface coffee;
simpleCoffee implements coffee;
espresso implements coffee;
coffeeDecorator implements coffee,有一个 coffee 类型的 属性 decoratedCoffee;
milkDecorator extends coffeeDecorator,其构造方法会产生一个coffee 类型的对象 ;
sugarDecorator extends coffeeDecorator,其构造方法会产生一个coffee 类型的对象 ;
new milkDecorator (new simpleCoffee ())
这样 milkDecorator 就可以修饰,通过自己构造函数获得的 coffee 类型的对象了 ,
同样 new sugarDecorator (new simpleCoffee ()), milkDecorator 也可以修饰,通过自己构造函数获得的 coffee 类型的对象了 
适用于,不需要修改原有对象的代码,就能动态的给原有对象添加新功能的场景,

各个模式之间的对比
适配器模式和装饰器模式的区别
虽然都是用于类的扩展和变换,但适配器模式更注重接口的转换和功能的实现;而装饰器模式则更注重对象的动态性在运行时动态地为对象添加新的职责或行为。

装饰器模式和代理模式的区别
装饰器模式给被装饰的对象增加功能,提供装饰功能,
代理模式不给对象增强功能,强调的是对一个对象的控制,提供代理功能

October2024the08thTuesday
补充
一个非常好的例子,关于适配器模式的实验代码,如下图
在这里插入图片描述

public interface TargetInterface {
void request(); }

public class Adaptee {
public void specificRequest() {
System.out.println(“Adaptee specific request.”); }
}

public class Adapter implements TargetInterface {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee; }
@Override
public void request() {
adaptee.specificRequest(); }
}

public class Client {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
TargetInterface target = new Adapter(adaptee);
target.request(); // 输出: Adaptee specific request.
} }

把上面的代码稍加修改,再放到可视化代码网站里去就能直接运行了,得到可视化代码图,能帮助很好的理解代码前后的关系,修改后的代码如下,


 interface TargetInterface{
   void request(); 
  }

 class Adaptee {
   public void specificRequest() {
     System.out.println("Adaptee specific request.");
    }
  }
 
 class Adapter implements TargetInterface {
   private Adaptee adaptee;
   public Adapter(Adaptee adaptee) {
     this.adaptee = adaptee; 
    }
   @Override
   public void request() {
     adaptee.specificRequest();
    } 
  }

 
 public class Client {
   public static void main(String[] args) {
     Adaptee adaptee = new Adaptee();
     TargetInterface target = new Adapter(adaptee);
     target.request();  // 输出: Adaptee specific request.
    }
  }

使用 Python Tutor: Visualize code 运行class Client得到最终结果,如下图
在这里插入图片描述
中间过程的可视化也很有意思,一并截取,如下:

过程1.如下:
在这里插入图片描述

过程2,如下:
在这里插入图片描述

过程3,如下:
在这里插入图片描述

过程4,如下:
在这里插入图片描述

过程5,如下:
在这里插入图片描述

最终结果,如下图
在这里插入图片描述

Python Tutor: Visualize code 可视化代码网址,如下:
https://pythontutor.com/render.html#mode=edit

October2024the08thTuesday

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值