设计模式—桥接模式
(部分转载或抄袭自: https://www.cnblogs.com/chenssy/p/3317866.html,
https://blog.csdn.net/a19881029/article/details/80979200)
1 桥接模式
将抽象化与实现分离,使二者可以独立的变化。
合成/聚合复用原则:面向对象设计的一个重要原则,尽量使用合成/聚合,不使用继承。
《大话设计模式》中的例子:手机品牌下面派生了华为和苹果等手机品牌, 华为又派生了华为的通信录,华为的游戏;苹果又派生了苹果的通信录和游戏。如果再加入新的品牌中兴,还要再派生中兴的通信录和游戏。而中兴和华为的游戏可能是一样的。在这个案例中,手机品牌在变化,软件也在变化,其实是两个维度都在变化。如果用继承的话,因为两个维度都变化,会很麻烦。解决方案:隔离两个维度变化之间的影响,手机品牌是接口,派生出各种手机品牌;手机软件作为接口,各种软件继承它。手机品牌的一个成员是手机软件,相当于聚合,这样二者可以互相独立变化。
上述方案中,表示和实现解耦,两者可以独立的变化,abstraction(手机品牌)中维护一个abstraction Impelemnt(软件)的指针,需要采用不同的实现方式的时候只需要传入不同的实例就可以了。
适用场景:手机的品牌是变化的(不断的有新品牌加入进来),手机的功能也变化(游戏,通信录,视频,...),如果不分离的话,在原来的继承体系上,每增加一个功能,或者每次增加一个品牌,都要加入好多类。比如我加入个小米手机,就需要加入各种小米的功能。 这种多个变化因素在多个对象之间共享,可以将这些变化的部分抽象出来再聚合进去。
优点: 依赖倒置; 将可以共享的部分,抽象出来,用聚合代替继承。减少代码重复。
缺点: 客户必须清楚的知道选择哪一种类型的实现。
注意:什么时候用继承,什么时候用聚合? 看变化是几维的,这里有2个维度,品牌和功能,如果只有一个维度,用继承就可以。
2 例子1:
先来一个一般的例子(就是不好理解,不知所云的例子),后面还有个不一般的例子。
一个画板的形状可以是圆形的,正方形的,颜色可以是红色,绿色。
class Implementor { public: virtual void Show() {} }; class Implementor1 : public Implementor { public: virtual void Show() { XXXXXX; } }; class Implementor2 :public Implementor { public: virtual void show() { XXXXXX } }; class Abstractor { Implementor * _implementor; public: virtual void SetImplementor(Implementor* ot) { this->_iplementor = ot; } virtual void operatoe() { _implementor->Show(); } }; class RefindAbstractor: public Abstractor { }; int mian() { Implementor* im1 = new Implementor1(); Implementor2 * im2 = new Implementor2(); RefinedAbstractor *re = new ReefindeAbstractor(); re->SetImplementor(im1); re->operate(); re->SetIpeementor(im2); re->operate(); }
2 例子2:
(转载自: https://www.cnblogs.com/chenssy/p/3317866.html)
这是一个不一般的(比较通俗易懂的)例子,所以直接搬过来(发现人家是原创,图片上有名字,所以明确写明是转载。)
以前老是讲继承的时候最爱用画图说事,这里有一个画笔,可以画正方形、长方形、圆形(熟悉的味道...)。但是现在我们需要给这些形状进行上色,这里有三种颜色:白色、灰色、黑色。这里我们可以画出3*3=9中图形:白色正方形、白色长方形、白色圆形。。。。。。到这里了我们几乎到知道了这里存在两种解决方案:
方案一:为每种形状都提供各种颜色的版本。(根据经验,这一定四最Stupid方案)。
假如我们添加椭圆,我们是不是又要增加三种颜色呢?假如我们在增加一个绿色,我们就要增加其四种形状了,继续加。继续加……每次增加都会增加若干个类(如果增加颜色则会增加形状个数个类,若增加形状则会增加颜色个数个类),这样的情况我想每个程序员都不会想要吧!那么我们看方案二。
方案二:根据实际需要对颜色和形状进行组合。
提供两个父类一个是颜色、一个形状,颜色父类和形状父类两个类都包含了相应的子类,然后根据需要对颜色和形状进行组合。
桥接模式将继承关系转化成关联关系,它降低了类与类之间的耦合度,减少了系统中类的数量,也减少了代码量。
首先是形状类:该类为一个抽象类,主要提供画形状的方法:Shape.java
public abstract class Shape { Color color; public void setColor(Color color) { this.color = color; } public abstract void draw(); }
然后是三个形状 。圆形:Circle.java
public class Circle extends Shape{ public void draw() { color.bepaint("正方形"); } }
长方形:Rectangle.java
public class Rectangle extends Shape{ public void draw() { color.bepaint("长方形"); } }
正方形:Square.java
public class Square extends Shape{ public void draw() { color.bepaint("正方形"); } }
颜色接口:Color.java
public interface Color { public void bepaint(String shape); }
白色:White.java
public class White implements Color{ public void bepaint(String shape) { System.out.println("白色的" + shape); } }
灰色:Gray.java
public class Gray implements Color{ public void bepaint(String shape) { System.out.println("灰色的" + shape); } }
黑色:Black.java
public class Black implements Color{ public void bepaint(String shape) { System.out.println("黑色的" + shape); } }
客户端:Client.java
public class Client { public static void main(String[] args) { //白色 Color white = new White(); //正方形 Shape square = new Square(); //白色的正方形 square.setColor(white); square.draw(); //长方形 Shape rectange = new Rectangle(); rectange.setColor(white); rectange.draw(); } }
3 例子3:
最后,看一下《大话设计模式》中多个手机品牌上面执行多个软件的例子吧:
首先定义一个不同软件的抽象接口。
public interface Software { public void run(); } public class AppStore implements Software { @Override public void run() { System.out.println("run app store"); } } public class Camera implements Software { @Override public void run() { System.out.println("run camera"); } }
手机品牌的抽象接口:注意,里面包含了一个Software 的引用。
public abstract class Phone { protected Software software; public void setSoftware(Software software){ this.software = software; } public abstract void run(); } public class Oppo extends Phone { @Override public void run() { software.run(); } } public class Vivo extends Phone { @Override public void run() { software.run(); } }
继承是一种强耦合关系,子类的实现与它的父类有非常紧密的依赖关系,父类的任何变化 都会导致子类发生变化,因此继承或者说强耦合关系严重影响了类的灵活性,并最终限制了可复用性
从桥接模式的设计上我们可以看出聚合是一种比继承要弱的关联关系,手机类和软件类都可独立的进行变化,不会互相影响
转载:https://www.cnblogs.com/chenssy/p/3317866.html
https://blog.csdn.net/a19881029/article/details/80979200