文章目录
- 二、结构型模式
- 1. 适配器模式
- 2. 桥接模式
- 3. 组合模式
- 4. 装饰模式
- 5. 外观模式
- 6. 代理模式
二、结构型模式
1. 适配器模式
适配器是用来将两个原本并不兼容的接口能够在一起工作。就像我们的充电线可以让手机接口和插座接口相互适应,完成工作。
课本上的案例是让机器人模仿其他动物叫,其实就是想让机器人能够适配不同动物的叫声,那么中间必定需要一个桥梁去完成这件事情,这个桥梁就叫做适配器,机器人叫做被适配者。
适配原理很简单,适配器类要实现机器人接口,然后重写机器人类中的叫方法,何为模仿?就是说你咋叫我就咋叫,那么重写机器人的叫方法里面是不是应该写那些动物的叫声呢?当然我们肯定不可能直接打印输出,那就没意义了,而是在这里面调用其它动物的叫方法,没有动物对象是不能调用其方法的,所以在适配器类中,我们还需 new 动物对象,然后在方法里面调用其方法。
以上例子是单方面地适配也就是单向适配,而我们的实验中又提到了一种双向适配。就是说猫可以学狗的一些行为,狗也可以学习猫的一些行为,它们之间是互相学习的,两个抽象类互为抽象目标和抽象适配者。在适配器类中new一个小猫对象和一个小狗对象,并重写它们的所有抽象方法,互相学习的过程就是互相调用对方方法的过程。
适配器模式将现有的接口需要转化为客户类期望的接口,使接口不兼容的那些接口可以一起工作,保证了对现有类的重用。
整体结构:
① 一个适配器类,双向适配要实现互相适配的两个接口;
② 两个抽象类,即互相学习的猫和狗;
③ 两个具体适配者兼目标类,重写抽象类中的所有方法。
//适配器类
package com.zxetest1;
public class Adapter implements Cat, Dog {
private Cat cat = new ConcreteCat();
private Dog dog = new ConcreteDog();
@Override
public void cry() {
System.out.print("小猫学");
dog.wang();
}
@Override
public void catchMouse() {
cat.catchMouse();
}
@Override
public void wang() {
dog.wang();
}
@Override
public void action() {
System.out.print("小狗学");
cat.catchMouse();
}
}
//抽象类
public interface Cat {
public void cry();
public void catchMouse();
}
public interface Dog {
public void wang();
public void action();
}
//具体适配者兼目标类
public class ConcreteCat implements Cat {
@Override
public void cry() {
System.out.println("小猫喵喵喵~");
}
@Override
public void catchMouse() {
System.out.println("小猫捉老鼠啦!");
}
}
public class ConcreteDog implements Dog {
@Override
public void wang() {
System.out.println("小狗汪汪汪~");
}
@Override
public void action() {
System.out.println("小狗吃骨头!");
}
}
2. 桥接模式
上课的时候,老师拿毛笔和蜡笔来举例的,题目大概是这样的:
现需要提供大中小 3 种型号的画笔,能够绘制 5 种不同的颜色。如果使用蜡笔,我们需要准备 15 支蜡笔,也就是说必须准备 15 个具体的蜡笔类。而如果使用毛笔的话,只需要 3 种型号的毛笔,外加 5 个颜料盒,用 8 个类就可以实现 15 支蜡笔的功能。
如果系统中某个类存在两个独立变化的维度,通过桥接模式可以将这两个维度分离出来,使得两者可以独立扩展。桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联取代了传统的多重继承,将类之间的静态继承关系转换为动态的对象组合关系,使得系统更加灵活,并易于扩展,同时有效地控制了系统中类的个数。
整体结构:
① 一个抽象类,引入实现类并提供 add 方法;
② 数个抽象类的子类,完成对实现类中 beAdd 方法的调用;
③ 一个实现类接口 ,内置 beAdd 方法;
④ 数个具体的实现类,重写 beAdd 方法。
public abstract class Food {
//水果
Fruit fruit;
public void setFruit(Fruit fruit) {
this.fruit = fruit;
}
//制作时添加
public abstract void add();
}
public class Cake extends Food {
@Override
public void add() {
fruit.beAdd("蛋糕");
}
}
public class Milk extends Food {
@Override
public void add() {
fruit.beAdd("牛奶");
}
}
public interface Fruit {
//被添加
public void beAdd(String food);
}
public class Banana implements Fruit {
@Override
public void beAdd(String food) {
System.out.println("香蕉" + food);
}
}
public class Mango implements Fruit {
@Override
public void beAdd(String food) {
System.out.println("芒果" + food);
}
}
3. 组合模式
组合模式就像课本上的盘子和水果的关系,又或实验里的文件夹和文件的关系,一个文件夹里可以有很多文件,也可以有更多的文件夹。
访问的时候自然就用到递归进行遍历,俗称套娃模式,一层一层地执行,到最后剩下的就是文件不能再分解,而文件就是我们最终想要的结果,这里一个文件夹里面的所有内容我们是用集合来接收的,以便后续遍历。
组合模式通过一种巧妙的设计方案使得用户可以一致性地处理整个树形结构或者树形结构的一部分,它描述了如何将容器对象和叶子对象进行递归组合,使得用户在使用时无需对它们进行区分,可以一致性地对待容器对象和叶子对象。
整体结构:
① 一个抽象文件类,既包括文件也包括文件夹,内部提供一个展示方法;
② 数个具体文件类,继承抽象文件类,最终的展示结果;
③ 一个具体文件夹类,继承抽象文件类,用 List 集合来存放文件夹下的东西,当调用展示方法的时候,内部使用遍历的方式去调用集合中每一个元素的展示方法,如果该元素是一个文件,那么它会直接输出,如果该元素是一个文件夹,那么又回到了文件夹的操作上,一层一层展开,直至最后仅剩下文件,输出结果。
public abstract class AbstractFile {
public abstract void display();
}
public class VideoFile extends AbstractFile {
@Override
public void display() {
System.out.println("这是一个视频文件!");
}
}
public class TextFile extends AbstractFile {
@Override
public void display() {
System.out.println("这是一个文本文件!");
}
}
public class Folder extends AbstractFile {
private ArrayList<AbstractFile> arrayList = new ArrayList<>();
public void add(AbstractFile file) {
arrayList.add(file);
}
public void remove(AbstractFile file) {
arrayList.remove(file);
}
@Override
public void display() {
for (AbstractFile a : arrayList) {
a.display();
}
}
}
4. 装饰模式
我们生活中的装饰就是在不改变原有模样的基础上,去为它装扮一些新的东西。
在设计模式中,装饰模式出现的动机是,可以在不改变一个对象本身功能的基础上给对象增加额外的新行为。它是一种用于替代继承的技术,它通过一种无须定义子类的方式给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系,引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩展原有类的功能。
整体结构:
① 一个抽象构建类,内置基本操作方法;
② 数个具体构建类,重写抽象构建内中的方法;
③ 一个抽象装饰类,继承抽象构建类,使用带参构造的方法传入一个具体构建,然后实现原有方法;
④ 数个具体装饰类,新增业务方法,重写父类方法,并在重写方法中调用原有业务方法及新增业务方法。
public abstract class Component {
public abstract void operation();
}
public class ConcreteComponent extends Component {
public void operation() {
//实现基本功能
}
}
public class Decorator extends Component {
private Component component; //维持一个对抽象构件对象的引用
//注入一个抽象构件类型的对象
public Decorator(Component component) {
this.component=component;
}
public void operation() {
component.operation(); //调用原有业务方法
}
}
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
public void operation() {
super.operation(); //调用原有业务方法
addedBehavior(); //调用新增业务方法
}
//新增业务方法
public void addedBehavior() {
……
}
}
5. 外观模式
通俗一点地理解,它有点像我们电脑的开关机,当你按了开关机键之后,就可以统一控制电脑内部各程序的运行与结束。就是说我现在只用一个开关就可以控制全部软件及硬件的运行。
在外观模式中,我们也会设置一个这样的开关,客户端直接使用这个开关来控制所有的方法。
外观模式为复杂的子系统调用提供一个统一的入口,使子系统与客户端的耦合度降低,且客户端调用非常方便。
整体结构:
① 数个子系统类,即开关要管理的对象;
② 一个总开关类,在无参构造方法里面 new 子系统对象,因为我们要调用这些子系统的方法,提供一个总开关入口方法,在该方法里面调用各子系统的方法,实际上就是把对子系统的调用步骤从客户端转移到了总开关类中。
public class Memory {
public void check() {
System.out.println("你好主人,内存正在自检中,请稍等!");
}
}
public class CPU {
public void run() {
System.out.println("你好主人,CPU 跑起来了!");
}
}
public class HardDisk {
public void read() {
System.out.println("你好主人,硬盘正在努力读取中,请稍等!");
}
}
public class OS {
public void load() {
System.out.println("你好主人,操作系统正在加载中,忙坏了!");
}
}
public class Mainframe {
private Memory memory;
private CPU cpu;
private OS os;
private HardDisk hardDisk;
public Mainframe() {
memory = new Memory();
cpu = new CPU();
os = new OS();
hardDisk = new HardDisk();
}
public void on() {
memory.check();
cpu.run();
os.load();
hardDisk.read();
}
}
6. 代理模式
代理即代替别人来完成一些事情。
代理模式是常用的结构型设计模式之一,当直接访问某些对象存在问题时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与代理对象需要实现相同的接口。
通过引入一个新的对象来实现对真实对象的操作,戒者将新的对象作为真实对象的一个替身。
整体结构:
① 一个抽象主题类,一般为接口;
② 数个目标类,实现接口;
③ 数个代理类,实现上述接口,是对被代理角色的增强。
public interface Movie {
void play();
}
public class RealMovie implements Movie {
@Override
public void play() {
System.out.println("您正在观看电影 《肖申克的救赎》");
}
}
public class Cinema implements Movie {
RealMovie movie;
public Cinema(RealMovie movie) {
super();
this.movie = movie;
}
@Override
public void play() {
guanggao(true); // 代理类的增强处理
movie.play(); // 代理类把具体业务委托给目标类,并没有直接实现
guanggao(false); // 代理类的增强处理
}
public void guanggao(boolean isStart){
if ( isStart ) {
System.out.println("电影马上开始了,爆米花、可乐、口香糖9.8折,快来买啊!");
} else {
System.out.println("电影马上结束了,爆米花、可乐、口香糖9.8折,买回家吃吧!");
}
}
}
Cinema 就是代理对象,它有一个 play() 方法。不过调用 play() 方法时,它进行了一些相关利益的处理,那就是广告。也就是说,代理类与目标类都可以播放电影,但是除此之外,代理类还对 “播放电影” 这个行为进行进一步增强,即增加了额外的处理,同时不影响目标类的实现。