设计模式之结构性模式(二)
三、桥接模式
四、组合模式
五、装饰模式
三、桥接模式
如果用多层继承结构实现下图关系,有如下问题:
1)扩展性问题
2)违反单一职责原则
将其分为两个维度:
类型维度和品牌维度
Computer2
/**
* 电脑类型
*/
package com.bridge;
public class Computer2 {
//品牌
protected Brand brand;
public Computer2(Brand brand) {
this.brand = brand;
}
public void sale() {
brand.sale();
}
}
//台式机
class Desktop2 extends Computer2 {
public Desktop2(Brand brand) {
super(brand);
}
public void sale() {
super.sale();
System.out.println("销售台式机");
}
}
//笔记本
class Laptop2 extends Computer2 {
public Laptop2(Brand brand) {
super(brand);
}
public void sale() {
super.sale();
System.out.println("销售笔记本");
}
}
//平板
class Pad2 extends Computer2 {
public Pad2(Brand brand) {
super(brand);
}
public void sale() {
super.sale();
System.out.println("销售平板");
}
}
Brand
/**
* 品牌类型
*/
package com.bridge;
public interface Brand {
public void sale();
}
class Lenovo implements Brand {
@Override
public void sale() {
System.out.println("销售联想电脑");
}
}
class Dell implements Brand {
@Override
public void sale() {
System.out.println("销售戴尔电脑");
}
}
Client
package com.bridge;
public class Client {
public static void main(String[] args) {
Computer2 c = new Desktop2(new Lenovo());
c.sale();
}
}
结果:
销售联想电脑
销售台式机
桥接模式总结:
1)可以取代多层继承方案,多层继承违背了单一职责原则,复用性差,类个数多;桥接模式极大减少了子类的个数,降低管理和维护成本
2)桥接模式提高了系统的可扩展性,在两个变化的维度中任意变化一个维度,都不需要修改原有的系统,符合开闭原则
桥接模式应用场景:
1)JDBC驱动程序
2)AWT中的Peer架构
3)银行日志管理
格式分类:操作日志、交易日志、异常日志
距离分类:本地记录日志、异地记录日志
4)人力资源系统中的奖金奖金计算模块
5)OA系统中的消息处理
四、组合模式
使用场景:把部分和整体的关系用树形结构来表示,从而使客户端可以使用统一的方式处理部分对象和整体对象
核心:
抽象挂件(Component)角色:定义了叶子和容器构建的共同点
叶子(Leaf)构件角色:无子节点
容器(Composite)构件角色:有容器特征,可以包含叶子节点
组合模式工作流程分析:
1)组合模式为处理树形结构提供了完美的解决方案,描述如何将容器和叶子进行递归组合,使得用户在使用时可以一致性的对待容器和叶子。
2)当容器对象的指定方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员,并执行调用,其中,使用了递归调用的机制对整个结构进行处理
实例:模拟杀毒软件架构设计
Conponent
/**
* 抽象组件
*/
package com.composite;
public interface Component {
public void operation();
}
//叶子组件
interface Leaf extends Component {
}
//容器组件
interface Composite extends Component {
void add(Component c);
void remove(Component c);
Composite getChild(int index);
}
AbstractFile
package com.composite;
import java.util.ArrayList;
import java.util.List;
//抽象构建
public interface AbstractFile {
void killVirus();//查毒
}
//文本文件
class TextFile implements AbstractFile {
private String name;
public TextFile(String name) {
this.name = name;
}
@Override
public void killVirus() {
System.out.println("---文本文件:"+this.name+",进行查杀");
}
}
//图像文件
class ImageFile implements AbstractFile {
private String name;
public ImageFile(String name) {
this.name = name;
}
@Override
public void killVirus() {
System.out.println("---图像文件:"+this.name+",进行查杀");
}
}
//视频文件
class VedioFile implements AbstractFile {
private String name;
public VedioFile(String name) {
this.name = name;
}
@Override
public void killVirus() {
System.out.println("---视频文件:"+this.name+",进行查杀");
}
}
//文件夹
class Folder implements AbstractFile {
private String name;
//容器
private List<AbstractFile> list = new ArrayList<AbstractFile>();
public Folder(String name) {
this.name = name;
}
//添加
public void add(AbstractFile file) {
list.add(file);
}
//移除
public void remove(AbstractFile file) {
list.remove(file);
}
//获取
public AbstractFile getChild(int index) {
return list.get(index);
}
@Override
public void killVirus() {
System.out.println("---文件夹:"+this.name+",进行查杀");
//遍历容器
for(AbstractFile file:list) {
file.killVirus();
}
}
}
Client
package com.composite;
public class Client {
public static void main(String[] args) {
AbstractFile f1,f2,f3;//文件
Folder f = new Folder("我的收藏");//文件夹
Folder f11 = new Folder("我的电影");//文件夹
//文件
f1 = new TextFile("hello.txt");
f2 = new ImageFile("111.jpg");
f3 = new VedioFile("视频");
//将各个文件加入到文件夹中
f.add(f1);
f.add(f2);
f11.add(f3);
f.add(f11);
//查杀文件夹
f.killVirus();
}
}
结果
---文件夹:我的收藏,进行查杀
---文本文件:hello.txt,进行查杀
---图像文件:111.jpg,进行查杀
---文件夹:我的电影,进行查杀
---视频文件:视频,进行查杀
组合模
式应用场景:
1)操作系统的资源管理器
2)GUI中的容器层次图
2)XML文件解析
4)OA系统中组织结构的处理
5)Junit单元测试框架
底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器)、Test接口(抽象)
五、装饰模式
职责:
1)动态的为一个对象增加新的功能
2)装饰模式是一种代替继承的技术,无需通过继承增加子类就能扩展性功能,使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀
实现细节:
1)Component抽象构件角色
真实对象和装饰对象有相同的接口,因此客户端对象能够以与真实对象相同的方式同装饰对象交互
2)ConcreteComponent具体构件角色(真实对象)
io流中的FileInputStream、FileOutputStream
3)Decorator装饰角色
持有一个抽象构件的引用,装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象,因此就能在真是对象调用后增加新的功能
4)ConcreteDecorato具体装饰角色
负责给构件对象增加新的责任
实例:车
实现细节:
package com.decorate;
//抽象构件角色Component
public interface Icar {
public void move();
}
//具体构件角色ConcreteComponent
class Car implements Icar {
@Override
public void move() {
System.out.println("陆地上跑");
}
}
//Decorate装饰角色
class SuperCar implements Icar {
//protected供子类使用
protected Icar car;
public SuperCar(Icar car) {
super();
this.car = car;
}
@Override
public void move() {
car.move();
}
}
//具体装饰角色ConcreteDecorate
class FlyCar extends SuperCar {
public FlyCar(Icar car) {
super(car);
}
public void fly() {
System.out.println("天上飞");
}
@Override
public void move() {
super.move();
fly();
}
}
class WaterCar extends SuperCar {
public WaterCar(Icar car) {
super(car);
}
public void swim() {
System.out.println("水上游");
}
@Override
public void move() {
super.move();
swim();
}
}
class AICar extends SuperCar {
public AICar(Icar car) {
super(car);
}
public void autoRun() {
System.out.println("自动跑");
}
@Override
public void move() {
super.move();
autoRun();
}
}
Client
package com.decorate;
public class Client {
public static void main(String[] args) {
Car car = new Car();
car.move();
System.out.println("------增加新的功能:天上飞------");
FlyCar flyCar = new FlyCar(car);
flyCar.move();
System.out.println("------增加新的功能:水上游------");
WaterCar waterCar = new WaterCar(car);
waterCar.move();
System.out.println("------增加两个新的功能:自动天上飞------");
AICar aiCar = new AICar(new FlyCar(car));
aiCar.move();
}
}
结果
陆地上跑
------增加新的功能:天上飞------
陆地上跑
天上飞
------增加新的功能:水上游------
陆地上跑
水上游
------增加两个新的功能:自动天上飞------
陆地上跑
天上飞
自动跑
IO流实现细节:
1)Conponent抽象构件角色
io流中InputStream、OutputStream、Reader、Writer
2)ConcreteComponent具体构件角色
io流中FileInputStream、FileOutputStream
3)DEcorator装饰角色
持有一个抽象构件的引用:io流中的FilterInoutStream、FilterOutputStream
4)ConcreteDecorator具体装饰角色
负责给构件对象增加新的责任,io流中的BufferdInputStream、BufferedOutputStream
开发中使用场景:
1)io输入流和输出流的设计
2)Swing包中图形界面构建功能
3)Servlet API中提供一个request对象的Decorator设计模式的默认实现类HttpServletrequestWrapper,增强request对象功能
总结:装饰模式也叫包装器模式;装饰模式降低系统的耦合度,可以动态的增加或删除对象职责,并使得需要装饰的具体构件类和具体装饰类可以独立变化,一边则更加新的具体构件类和具体装饰类
优点:
1)扩展对对象功能,比继承灵活,不会导致类个数急剧增加
2)可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象
3)具体构件类和具体装饰类可以独立变化,,用户可以根据自己需要增加新的具体构件子类和具体装饰子类
缺点:
1)产生很多小对象,大量小对象占据内存,一定程度上影响性能
2)装饰模式易于出错,调试排查比较麻烦
装饰模式和桥接模式的区别:
都是为了解决过多子类对象问题,桥接模式是对象自身现有机制沿着多个维度变化,有部分不稳定;装饰模式是为了增加新的功能