目录
一、前言
常用的设计模式里面很多设计模式非常相似,学习者在学习的时候容易云里雾里,如果采用对比的方式学习,可以更加清晰的看出相似设计模式的异同。
网上介绍设计模式的文章有很多,但是一般都是介绍一种设计模式的,每个设计模式举例子都不同,读者很难形成对比性学习。
笔者致力于使用对比、抽取出设计模式的异同来介绍设计模式。本文使用同一个例子来讲述桥接模式、代理模式、装饰器模式,这三种模式都遵循“优先使用组合而不是继承”的设计原则。(使用一个例子是帮助读者从根本上区分三种设计模式)
二、桥接模式
官方定义:Decouple an abstraction from its implementation so that the two can vary independently.(将抽象和实现解耦,使得两者可以独立的变化。)
我的理解:桥接模式中抽象和实现是两个不同的维度,相互独立,通过组合的关系联系在一起。
每个人都有灵魂soul和肉体body,由灵魂来产生想法,由肉体来支配活动,任何一个活动或动作的产生需要灵魂和肉体同时参与,所以必须使用一种方式将灵魂和肉体组合到一起,一起操作,从业务逻辑意义上来看,因为灵魂是内在的,肉体是外在的,所以将灵魂的对象引用注入到肉体中(这是使用属性注入),这样一来,客户端只要操作肉体即可(因为灵魂注入到肉体中,所有通过操作肉体来同时操作两者)。
注意:灵魂和肉体是两个独立的维度,没有包含关系,也不会继承同一父类,两个独立维度是桥接模式的核心,也是后面桥接模式区别代理模式和装饰器模式的关键。
package mypackage1;
public class DesignPatternDemo {
public static void main(String[] args) {
Body body = new FootBall_Game("foot", new Playing_FootBall()); // 踢足球需要脚
body.sport();
body = new BasketBall_Game("hand", new Playing_BasketBall()); // 打篮球需要手
body.sport();
}
}
// 灵魂和肉体是两个不同的维度 不是父子继承关系 但是要组合在一起才能共同完成事情
interface Soul {
public void sport(); //灵魂产生思想,要参与运动
}
// 灵魂要参与运动 但是仅仅灵魂这一个维度没有办法去实现参与运动 比如 踢足球需要脚 打篮球需要手 但是手脚是属于肉体 所以必须把灵魂注入肉体中
class Playing_FootBall implements Soul {
@Override
public void sport() {
System.out.println("playing football");
}
}
class Playing_BasketBall implements Soul {
@Override
public void sport() {
System.out.println("playing basketball");
}
}
// 同样 仅仅肉体这一个维度也没有办法完成踢足球和打篮球的运动 因为肉体只有手脚 没有思想 不知道要具体参与什么运动
abstract class Body {
protected String organ;
Soul soul; //灵魂注入肉体
public Body(Soul soul) {
this.soul = soul;
}
abstract void sport();
}
class FootBall_Game extends Body {
public FootBall_Game(String organ, Soul soul) {
super(soul);
this.organ = organ;
}
@Override
void sport() {
soul.sport(); //肉体参与运动实际上是灵魂和肉体一起参与运动
}
}
class BasketBall_Game extends Body {
public BasketBall_Game(String organ, Soul soul) {
super(soul);
this.organ = organ;
}
@Override
void sport() {
soul.sport();
}
}
给出类图:
三、代理模式
官方定义:Provide a surrogate (代理) or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问。)
我的理解:实体类和代理类都是继承于与同一个父类,将实体类引用注入到代理类中,客户端通过操作代理类间接操作实体类。
新建一个ProxySoul(则之前的Soul就是实际灵魂),表示代理灵魂类,可以通过操作代理灵魂来操作实际灵魂Soul,代理灵魂ProxySoul和实际灵魂Soul都继承于InterfaceSoul类,所以它们是同一维度,这个代理模式区别桥接模式的重要特点。
package mypackage;
public class DesignPatternDemo {
//其实在刚才的桥接模式中,就是实际灵魂注入到肉体中,客户端操作肉体即可
//实际灵魂--肉体--客户端
//现在的代理模式就是,实际灵魂注入到代理灵魂中,然后运行时确定真正注入到肉体中的是代理灵魂,客户端操作肉体即可
//实际灵魂--代理灵魂--肉体--客户端
//仅仅中间多了一个代理灵魂类而已
public static void main(String[] args) {
Body body1=new FootBall_Game("foot",new ProxySoul(new Playing_FootBall())); // 踢足球需要脚
body1.sport();
body1=new BasketBall_Game("hand",new ProxySoul(new Playing_BasketBall()));// 打篮球需要手
body1.sport();
}
}
interface InterfaceSoul{
public void sport(); //灵魂产生思想,要参与运动
}
class ProxySoul implements InterfaceSoul{
public Soul _soul;//实际灵魂注入到代理灵魂类中
public ProxySoul(Soul _soul){
this._soul=_soul;
}
@Override
public void sport() { //代理灵魂没有具体的产生思想的类,踢足球Playing_FootBall还是打篮球Playing_BasketBall,它只要调用实际灵魂的这些产生思想的类就好了
_soul.sport();
}
}
class Soul implements InterfaceSoul {
@Override
public void sport() {}
}
class Playing_FootBall extends Soul {
@Override
public void sport() {
System.out.println("playing football");
}
}
class Playing_BasketBall extends Soul {
@Override
public void sport() {
System.out.println("playing basketball");
}
}
abstract class Body {
protected String organ;
InterfaceSoul soul; //灵魂注入肉体 这里是面向接口编程,运行时是代理灵魂
public Body(InterfaceSoul soul) {
this.soul = soul;
}
abstract void sport();
}
class FootBall_Game extends Body {
public FootBall_Game(String organ, InterfaceSoul soul) {
super(soul);
this.organ = organ;
}
@Override
void sport() {
soul.sport(); //肉体参与运动实际上是灵魂和肉体一起参与运动
}
}
class BasketBall_Game extends Body {
public BasketBall_Game(String organ, InterfaceSoul soul) {
super(soul);
this.organ = organ;
}
@Override
void sport() {
soul.sport();
}
}
给出类图:
四、装饰器模式
官方定义:Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。)
我的理解:实体类和装饰器类都是继承于与同一个父类,将实体类引用注入到装饰器类中,客户端通过操作装饰器类间接操作实体类。
装饰器模式就是给现有的类添加新的属性、新的方法,但是不能在现有类上做修改。所以只有两种方式,继承或组合:继承,新建一个类继承现有的类,然后给新类添加属性或方法;组合,新建一个类,将现有类引用注入新类,在新类中新增属性或方法。由于设计模式的原则是优先使用组合而非继承,所以我们要讲的装饰器模式是用组合方式实现的。
新建一个SoulDecorator,表示灵魂装饰器,可以通过操作灵魂装饰器类来操作实际灵魂Soul,灵魂装饰器SoulDecorator和实际灵魂Soul都继承于InterfaceSoul类,所以它们是同一维度,这个装饰器模式区别桥接模式的重要特点,而装饰方法extra()的存在是装饰器模式区别代理模糊的重要特点。
package mypackage;
public class DesignPatternDemo {
//其实在最开始的桥接模式中,就是实际灵魂注入到肉体中,客户端操作肉体即可
//实际灵魂--肉体--客户端
//============================
//后来的代理模式就是,实际灵魂注入到代理灵魂中,然后运行时确定真正注入到肉体中的是代理灵魂,客户端操作肉体即可
//实际灵魂--代理灵魂--肉体--客户端
//仅仅中间多了一个代理灵魂类而已
//============================
//现在的装饰器模式就是,实际灵魂注入到灵魂装饰类中,然后运行时确定真正注入到肉体中的是灵魂装饰类,客户端操作肉体即可
//实际灵魂--灵魂装饰类--肉体--客户端
//仅仅中间多了一个灵魂装饰类而已,而装饰类相对于代理的区别就是,代理是老老实实的代理,不添加额外操作,装饰器是要添加额外操作
public static void main(String[] args) {
Body body1=new FootBall_Game("foot",new SoulDecorator(new Playing_FootBall())); // 踢足球需要脚
body1.sport();
body1=new BasketBall_Game("hand",new SoulDecorator(new Playing_BasketBall()));// 打篮球需要手
body1.sport();
}
}
interface InterfaceSoul{
public void sport(); //灵魂产生思想,要参与运动
}
class SoulDecorator implements InterfaceSoul{
public Soul _soul;//实际灵魂注入到灵魂装饰类中
public SoulDecorator(Soul _soul){
this._soul=_soul;
}
@Override
public void sport() { //灵魂装饰类没有具体的产生思想的类,踢足球Playing_FootBall还是打篮球Playing_BasketBall,它只要调用实际灵魂的这些产生思想的类就好了
_soul.sport();
extra();
}
private void extra(){ //装饰器相比于代理,就是多了这么一个extra方法,起额外装饰之用
System.out.println("This is so interesting");
}
}
class Soul implements InterfaceSoul {
@Override
public void sport() {}
}
class Playing_FootBall extends Soul {
@Override
public void sport() {
System.out.println("playing football");
}
}
class Playing_BasketBall extends Soul {
@Override
public void sport() {
System.out.println("playing basketball");
}
}
abstract class Body {
protected String organ;
InterfaceSoul soul; //灵魂注入肉体 这里是面向接口编程,运行时是灵魂装饰类对象
public Body(InterfaceSoul soul) {
this.soul = soul;
}
abstract void sport();
}
class FootBall_Game extends Body {
public FootBall_Game(String organ, InterfaceSoul soul) {
super(soul);
this.organ = organ;
}
@Override
void sport() {
soul.sport(); //肉体参与运动实际上是灵魂和肉体一起参与运动
}
}
class BasketBall_Game extends Body {
public BasketBall_Game(String organ, InterfaceSoul soul) {
super(soul);
this.organ = organ;
}
@Override
void sport() {
soul.sport();
}
}
给出类图:
五、三者对比(桥接模式、代理模式、装饰器模式)
5.1 三脚架图
三脚架图如下:
5.2 表格:桥接模式、代理模式与装饰器模式对比
桥接模式 | 代理模式 | 装饰器模式 | |
关键类 | 多个维度,各个维度父类和子类 | 代理类 | 装饰类 |
继承关系 | 各个维度内父类和子类继承关系,便于面向接口编程,各个维度之间类与类没有关系,相互独立 | 实体类和代理类都继承于同一父类,它们与父类是继承关系,便于客户端面向接口编程 | 实体类和装饰类都继承于同一父类,它们与父类是继承关系,便于客户端面向接口编程 |
组合关系 | 各个维度之间类与类使用组合注入方式联系 | 实体类和代理类之间是组合关系,将实体类引用注入代理类,在代理类中调用实体类方法或属性 | 实体类和装饰类之间是组合关系,将实体类引用注入装饰类,在装饰类中调用实体类方法或属性,还有装饰类自己新增的方法和属性 |
联系 | 装饰器模式=代理模式+装饰方法 |
六、从扩展的角度来谈设计模式
任何一个设计模式,只要遵循了设计原则,要扩展起来都是非常容易的,通过扩展这个设计模式,我们可以真正学习到这个设计模式的精华,真正知道使用这个设计模式的好处。
6.1 桥接模式扩展
我们现在对第二部分的桥接模式扩展,加上一个打排球的类:
package mypackage;
public class DesignPatternDemo {
public static void main(String[] args) {
Body body = new FootBall_Game("foot", new Playing_FootBall());
body.sport();
body = new BasketBall_Game("hand", new Playing_BasketBall());
body.sport();
body = new VolleyBall_Game("hand", new Playing_VolleyBall());
body.sport();
}
}
// 灵魂和肉体是两个不同的维度 不是父子继承关系 但是要组合在一起才能共同完成事情
interface Soul {
public void sport();
}
// 灵魂要参与运动 但是仅仅灵魂这一个维度没有办法去实现参与运动 比如 踢足球需要脚 打篮球需要手 但是手脚是属于肉体 所以必须把灵魂注入肉体中
class Playing_FootBall implements Soul {
@Override
public void sport() {
System.out.println("playing football");
}
}
class Playing_BasketBall implements Soul {
@Override
public void sport() {
System.out.println("playing basketball");
}
}
class Playing_VolleyBall implements Soul {
@Override
public void sport() {
System.out.println("playing volleyball");
}
}
// 同样 仅仅肉体这一个维度也没有办法完成踢足球和打篮球的运动 因为肉体只有手脚 没有思想 不知道要具体参与什么运动
abstract class Body {
protected String organ;
Soul soul;
public Body(Soul soul) {
this.soul = soul;
}
abstract void sport();
}
class FootBall_Game extends Body {
public FootBall_Game(String organ, Soul soul) {
super(soul);
this.organ = organ;
}
@Override
void sport() {
soul.sport();
}
}
class BasketBall_Game extends Body {
public BasketBall_Game(String organ, Soul soul) {
super(soul);
this.organ = organ;
}
@Override
void sport() {
soul.sport();
}
}
class VolleyBall_Game extends Body {
public VolleyBall_Game(String organ, Soul soul) {
super(soul);
this.organ = organ;
}
@Override
void sport() {
soul.sport();
}
}
可以看到,只要增加一个“打排球的灵魂类”、“打排球的肉体类”即可,扩展起来非常方便,桥接模式完全遵循了“对扩展开放,对修改关闭”的设计原则。
给出类图:
6.2 代理模式扩展
我们的代理模式在6.1的基础在再次扩展,再增加一个代理肉体ProxyBody:
package mypackage;
public class DesignPatternDemo {
//实际灵魂--代理灵魂--实际肉体--代理肉体--客户端
public static void main(String[] args) {
InterfaceBody body=new ProxyBody(new FootBall_Game("foot",new ProxySoul(new Playing_FootBall())));// 踢足球需要脚
body.sport();
System.out.println("=================================");
body=new ProxyBody(new FootBall_Game("hand",new ProxySoul(new Playing_BasketBall())));// 打篮球需要手
body.sport();
System.out.println("=================================");
body=new ProxyBody(new FootBall_Game("hand",new ProxySoul(new Playing_VolleyBall())));// 打排球需要手
body.sport();
}
}
interface InterfaceSoul{
public void sport(); //灵魂产生思想,要参与运动
}
class ProxySoul implements InterfaceSoul{
public Soul _soul;//实际灵魂注入到代理灵魂类中
public ProxySoul(Soul _soul){
this._soul=_soul;
}
@Override
public void sport() { //代理灵魂没有具体的产生思想的类,踢足球Playing_FootBall还是打篮球Playing_BasketBall,它只要调用实际灵魂的这些产生思想的类就好了
_soul.sport();
}
}
class Soul implements InterfaceSoul {
@Override
public void sport() {}
}
class Playing_FootBall extends Soul {
@Override
public void sport() {
System.out.println("playing football");
}
}
class Playing_BasketBall extends Soul {
@Override
public void sport() {
System.out.println("playing basketball");
}
}
class Playing_VolleyBall extends Soul {
@Override
public void sport() {
System.out.println("playing volleyball");
}
}
interface InterfaceBody{
void sport();
}
class ProxyBody implements InterfaceBody{
protected Body _body;
public ProxyBody(Body _body){
this._body=_body;
}
@Override
public void sport() {
_body.sport();
}
}
abstract class Body implements InterfaceBody{
protected String organ;
InterfaceSoul soul; //灵魂注入肉体 这里是面向接口编程,运行时是代理灵魂
public Body(InterfaceSoul soul) {
this.soul = soul;
}
public abstract void sport();
}
class FootBall_Game extends Body {
public FootBall_Game(String organ, InterfaceSoul soul) {
super(soul);
this.organ = organ;
}
@Override
public void sport() {
soul.sport(); //肉体参与运动实际上是灵魂和肉体一起参与运动
}
}
class BasketBall_Game extends Body {
public BasketBall_Game(String organ, InterfaceSoul soul) {
super(soul);
this.organ = organ;
}
@Override
public void sport() {
soul.sport();
}
}
class VolleyBall_Game extends Body {
public VolleyBall_Game(String organ, InterfaceSoul soul) {
super(soul);
this.organ = organ;
}
@Override
public void sport() {
soul.sport();
}
}
可以看到,只要增加一个代理肉体类ProxyBody即可。
给出类图:
6.3 装饰器模式扩展
我们的装饰器模式在6.1的基础在再次扩展,再增加一个肉体装饰器BodyDecorator:
package mypackage;
public class DesignPatternDemo {
public static void main(String[] args) {
InterfaceBody body=new BodyDecorator(new FootBall_Game("foot",new SoulDecorator(new Playing_FootBall())));// 踢足球需要脚
body.sport();
System.out.println("=================================");
body=new BodyDecorator(new FootBall_Game("hand",new SoulDecorator(new Playing_BasketBall())));// 打篮球需要手
body.sport();
System.out.println("=================================");
body=new BodyDecorator(new FootBall_Game("hand",new SoulDecorator(new Playing_VolleyBall())));// 打排球需要手
body.sport();
}
}
interface InterfaceSoul{
public void sport(); //灵魂产生思想,要参与运动
}
class SoulDecorator implements InterfaceSoul{
public Soul _soul;//实际灵魂注入到灵魂装饰类中
public SoulDecorator(Soul _soul){
this._soul=_soul;
}
@Override
public void sport() { //灵魂装饰类没有具体的产生思想的类,踢足球Playing_FootBall还是打篮球Playing_BasketBall,它只要调用实际灵魂的这些产生思想的类就好了
_soul.sport();
extra();
}
private void extra(){ //装饰器相比于代理,就是多了这么一个extra方法,起额外装饰之用
System.out.println("This is so interesting");
}
}
class Soul implements InterfaceSoul {
@Override
public void sport() {}
}
class Playing_FootBall extends Soul {
@Override
public void sport() {
System.out.println("playing football");
}
}
class Playing_BasketBall extends Soul {
@Override
public void sport() {
System.out.println("playing basketball");
}
}
class Playing_VolleyBall extends Soul {
@Override
public void sport() {
System.out.println("playing volleyball");
}
}
interface InterfaceBody{
void sport();
}
class BodyDecorator implements InterfaceBody{
protected Body _body;
public BodyDecorator(Body _body){
this._body=_body;
}
@Override
public void sport() {
_body.sport();
extraBody();
}
public void extraBody(){
System.out.println("This body is so health");
}
}
abstract class Body implements InterfaceBody{
protected String organ;
InterfaceSoul soul; //灵魂注入肉体 这里是面向接口编程,运行时是灵魂装饰类对象
public Body(InterfaceSoul soul) {
this.soul = soul;
}
public abstract void sport();
}
class FootBall_Game extends Body {
public FootBall_Game(String organ, InterfaceSoul soul) {
super(soul);
this.organ = organ;
}
@Override
public void sport() {
soul.sport(); //肉体参与运动实际上是灵魂和肉体一起参与运动
}
}
class BasketBall_Game extends Body {
public BasketBall_Game(String organ, InterfaceSoul soul) {
super(soul);
this.organ = organ;
}
@Override
public void sport() {
soul.sport();
}
}
class VolleyBall_Game extends Body {
public VolleyBall_Game(String organ, InterfaceSoul soul) {
super(soul);
this.organ = organ;
}
@Override
public void sport() {
soul.sport();
}
}
可以看到,只要增加一个肉体装饰类BodyDecorator即可。
给出类图:
七、小结
本文使用同一个例子(Body && Soul)阐述桥接模式、代理模式、装饰器模式三者区别,相同点是三种设计模式都遵循了“优先使用组合而非继承”的设计原则,如桥接模式中soul引用注入Body类,代理模式中soul引用注入到ProxySoul类,装饰器模式中soul引用注入到SoulDecorator类;不同点在于桥接模式为多个维度,代理模式、装饰器模式继承于同一父类,而代理模式与装饰器模式区别在于是否具有额外的装饰方法。第六部分再次使用扩展的方式来看三种模式。
天天打码,天天进步!