桥接模式
定义: 将抽象部分与它的实现部分解耦(分离),使得两者都能够独立变化。
理解:
理论上任意一层都能作为主类,一般将物理层面的作为主类(抽象类
),通过set注入抽象部分(接口
)。
角色:
Abstraction(抽象类)
RefinedAbstraction(扩充抽象类/子类)
Implementor(实现类接口)
ConcreteImplementor(具体实现类)
拓展:
体现以下原则:
单一职责原则
、开闭原则
、合成复用原则
、里氏代换原则
、依赖倒置原则
类图:
代码:
接口:
public interface Driveimp {
public void doDrive();
}
具体子类:
public class LinuxImp implements Driveimp{
@Override
public void doDrive() {
System.out.println("Linux系统");
}
}
public class WindowsImp implements Driveimp{
@Override
public void doDrive() {
System.out.println("Windows系统");
}
}
抽象类(主类):
public abstract class Drive {
//添加功能即添加属性和SetXxx()
protected Driveimp driveimp; //"桥接"Driveimp接口属性,访问权限最好声明为 protected (聚合关系)。子类可直接使用
public void setDriveimp(Driveimp driveimp) {
this.driveimp = driveimp;
}
public abstract void printFile();//抽象方法,供子类调用
}
具体子类:
public class WordFile extends Drive {
@Override
public void printFile() {
driveimp.doDrive();//"Linux系统"/"Windows系统"
System.out.println("打印Word文件");
}
}
Client:
public class Client {
public static void main(String[] args) {
Drive wordFile = new WordFile();
// 添加的功能
// Driveimp windowsImp = new WindowsImp();
Driveimp linuxImp = new LinuxImp();
wordFile.setDriveimp(linuxImp);//父类set方法注入
wordFile.printFile();
}
}
结果: Linux系统 打印Word文件
优点:
(1)分离抽象接口及其实现部分
(2)可以取代多层继承方案,极大地减少了子类的个数
(3)提高了系统的可扩展性,符合开闭原则
缺点:
(1)会增加系统的理解与设计难度,
(2)正确识别出系统中两个独立变化的维度并不是一件容易的事情
适用环境:
(1)需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系
(2)抽象部分和实现部分可以以继承的方式独立扩展而互不影响
(3)一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立地进行扩展
(4)不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统
适配器模式
定义: 将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作
角色:
Target (抽象目标类)适配器
的父类
Adapter(适配器类)
Adaptee(适配者类)
类适配器:目标类为接口时
public class Adapter extends Adaptee implements Target{
public void request(){
super.specificRequest();
}
}
对象适配器:目标类为抽象类时
public class Adapter extends Target{
private Adaptee adaptee; //而适配者关联进来(关联关系)
public Adapter(Adaptee adaptee){ //构造注入
this.adaptee=adaptee;
}
public void request(){
adaptee.specificRequest();
}
}
类图:
代码:
抽象目标类
public abstract class CarController{
public void move(){
sout("汽车移动")
}
//抽象方法
public abstract void phonate(); //发出声音
public abstract void twinkle(); //灯光闪烁
}
适配者类
public class PoliceSound{
public void a(){
sout("发出警笛声")
}
}
public class PoliceLamp{
public void b(){
sout("警灯闪烁")
}
}
适配器类
public class MyAdapter extends CarController{
private PoliceSound policeSound; //声明适配者
private PoliceLamp policeLamp; //声明适配者
public MyAdapter(){
PoliceSound = new PoliceSound(); //创建适配者对象,根据情况选择不同的注入方式
policeLamp = new PoliceLamp();
}
public void phonate(){
policeSound.a(); //调用适配者方法
}
public void twinkle(){
policeLamp.b();
}
}
Client
public class Client {
public static void main(String[] args) {
MyAdapter myAdapter = new MyAdapter(); //只用使用适配器就行
myAdapter.move();
myAdapter.phonate();
myAdapter.twinkle();
}
}
结果:
汽车移动 发出警笛声 警灯闪烁
优点:
(1)将目标类和适配者类解耦,通过适配器类来重用适配者类,无须修改原有结构
(2)增加了类的透明性和复用性
(3)灵活性和扩展性非常好
缺点:
(1)一次最多只能适配一个适配者类,不能同时适配多个适配者
(2)适配者类不能为最终类
(3)目标抽象类只能为接口,不能为类
适用环境:
(1)系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码
(2)创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类(包括一些可能在将来引进的类)一起工作
装饰模式
定义: 动态地给一个对象增加一些额外的职责。就扩展功能而言,装饰者模式提供了一种比使用子类更加灵活的替代方案。(不用采用子类方式)
角色:
VisualComponen(抽象构件) 具体构件
和抽象装饰类
的共同父类
ListBox(具体构件 )
ComponentDecorator(抽象装饰类 )引用了抽象构件
ScrollBarDecorator(具体装饰类 )
类图:
代码:
抽象构件:
public abstract class VisualComponen {
public abstract void Display();
}
具体构件:
public class ListBox extends VisualComponen{
@Override
public void Display() {
System.out.println("列表框构件——显示列表框......(完成)!");
}
}
public class TextBox extends VisualComponen{
@Override
public void Display() {
System.out.println("文本框构件——显示文本框!");
}
}
抽象装饰类:
public class ComponentDecorator extends VisualComponen{
//维持对抽父类对象的引用。与桥接模式类似,区别:桥接双方无关系,而装饰者继承共同父类,与代理类似(聚合)
private VisualComponen component;
//方式一:构造方法注入对象
// public ComponentDecorator(VisualComponent component) {
// this.component = component;
// }
//方式二:set方法注入对象
public void setComponent(VisualComponen component) {
this.component = component;
}
// 实现注入构件的基本功能
@Override
public void Display() {
component.Display(); //调用注入的具体构件的方法
}
}
具体装饰类:
public class ScrollBarDecorator extends ComponentDecorator{
public ScrollBarDecorator(VisualComponen component) {
// super(component);
super.setComponent(component);
}
//再重写并新增加的方法,实现装饰构件的装饰:即实现构件的基本方法,也实现新增加的方法
@Override
public void Display() {
this.SetScrollBar();
super.Display();
}
//设置滚动条,此方法为装饰的增加的新方法
public void SetScrollBar() {
System.out.println("为构件增加滚动条......(完成)!");
}
}
public class BlackBorderDecorator extends ComponentDecorator{
public BlackBorderDecorator(VisualComponen component) { //构造方法
// super(component);
super.setComponent(component); //父类set方法
}
//再次重写基类的基本方式,实现装饰构件的装饰:即实现构件的基本方法,也实现新增加的方法
@Override
public void Display() {
this.SetBlackBorder();
super.Display();
}
//新增加的方法,实现黑色边框
public void SetBlackBorder() {
System.out.println("为构件增加黑色边框......(完成)!");
}
Client:
public class Client {
public static void main(String[] args) {
//声明
VisualComponen visualcomponent;
VisualComponen componentScrollBar;
VisualComponen componentBlackBorder;
//实例化具体构件类
visualcomponent = new ListBox();
System.out.println("构件开始准备------>:");
// 将具体构件类作为参数,进行滚动条装饰
componentScrollBar = new ScrollBarDecorator(visualcomponent);
//套娃调用,将装饰了一次之后的对象继续注入到另一个装饰类中,进行接下来的装饰
componentBlackBorder = new BlackBorderDecorator(componentScrollBar);
componentBlackBorder.Display(); //逆序输出(方法栈)
}
}
结果:
构件开始准备------>: 为构件增加黑色边框......(完成)! 为构件增加滚动条......(完成)! 列表框构件——显示列表框......(完成)!
优点:
(1)对于扩展一个对象的功能,装饰者模式比继承更加灵活
(2)可以动态的方式来扩展一个对象的功能,
(3)可以对一个对象进行多次装饰
(4)具体构件类与具体装饰者类可以独立变化,符合开闭原则
缺点:
(1)使用装饰者模式进行系统设计时将产生很多小对象,大量小对象的产生势必会占用更多的系统资源,在一定程度上影响程序的性能
(2)比继承更加易于出错,排错也更困难
适用环境:
(1)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
(2)当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰者模式
代理模式
定义: 给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
角色:
Subject(抽象主题角色)真实主题角色
和代理主题角色
的共同父类
RealSubject(真实主题角色)
Proxy(代理主题角色)
类图:(纠错:将聚合关系改成关联关系:直接new出来的)
代码:
抽象主题角色: (抽象类或者接口,建议使用接口,更灵活)
public abstract class Searcher {
abstract String DoSearch(String userId, String keyword);
}
真实主题角色:
public class RealSearcher extends Searcher{
//实现接口方法,模拟查询商务信息
@Override
String DoSearch(String userId, String keyword) {
System.out.println("用户: " + userId + " 查询业务信息!" +" 查询主要内容为 : "+ keyword );
return "查询内容详情";
}
}
代理主题角色:
public class ProxySearcher extends Searcher {
// 引用真实对象(和装饰者模式类似)
private RealSearcher searcher = new RealSearcher();
// 用户验证类
private AccessValidator validator;
// 日志记录类
private Logger logger;
//创建访问验证对象并调用其Validate()方法实现身份验证
private boolean Validate(String userId) {
validator = new AccessValidator();
return validator.Validate(userId);
}
//创建日志记录对象并调用其Log()方法实现日志记录
public void Log(String userId) {
logger = new Logger();
logger.Log(userId);
}
//代理类拓展收费方法
public void charge() {
System.out.println("收取查询费用");
}
//实现接口,执行查询操作:即通过调用真实对象的方法实现中
@Override
String DoSearch(String userId, String keyword) {
// 如果身份验证成功,则执行查询
if (this.Validate(userId)) {
String result = searcher.DoSearch(userId, keyword); // 调用真实主题对象的查询方法
this.Log(userId); // 记录查询日志
charge(); //收费功能
return result; // 返回查询结果
} else {
return null;
}
}
}
业务类:
public class AccessValidator {
/**
* 模拟实现登录验证
*
* @param userId
* @return
*/
public boolean Validate(String userId) {
System.out.println("用户验证中... " + userId + " 是否是合法用户?");
if (userId.equals("199000214王世祥")) {
System.out.println(userId + " 认证成功!");
return true;
} else {
System.out.println("认证失败!请重新登录" + userId);
return false;
}
}
}
public class Logger {
static int i=0;
// 模拟实现日志记录,加锁保证共享安全
public synchronized void Log(String userId) {
i++;
System.out.println("更新数据库,用户 " + userId + " 查询次数为:"+i);
}
}
Client:
public class Client {
public static void main(String[] args) {
//创建对象
Searcher searcher = new ProxySearcher();
//得到查询结果
String result = searcher.DoSearch("199000214王世祥", "大话设计模式");
}
}
结果:
用户验证中... 199000214王世祥 是否是合法用户? 199000214王世祥 认证成功! 用户: 199000214王世祥 查询业务信息! 查询主要内容为 : 大话设计模式 更新数据库,用户 199000214王世祥 查询次数为:1 收取查询费用
优点:
(1) 降低了系统的耦合度 (2) 符合开闭原则,系统具有较好的灵活性和可扩展性
缺点:
(1)由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢(例如保护代理)
(2)实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂(例如远程代理)
适用环境:
(1)当客户端对象需要访问远程主机中的对象时可以使用远程代理
(2)当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、
(3)当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理
(4)当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理
(5)当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理
拓展:
远程代理:
为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可以在另一台主机中,远程代理又称为大使(Ambassador)
虚拟代理:
如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建
保护代理:
控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限
缓冲代理:
为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果
智能引用代理:
当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等