源码git地址 https://github.com/dlovetco/designMode
问题提出
不同品牌的手机各不相同。同一款软件在不同手机上运行很有可能是不一样的。要求用代码实现这一种情况。
题目中提到了两种不同的分类方式:一种是按品牌分类手机,然后对于每一个软件都需要两个实体类来表示不同手机运行情况不同;另外一种是先按照软件种类分,然后再根据手机品牌分。这两种分类方法都有很明显的缺点,就是在种类过多时类的层次结构过于复杂。此外手机类和软件类之间的耦合也极强。
在介绍桥接模式之前,我们先学习一个新的原则。
合成/聚合复用原则
尽量使用合成/聚合,尽量不要使用类继承。
类继承往往会在不知不觉中形成一颗庞大的类“家族”。而且后期对于这种类“家族”也非常不容易维护。若改动一个父类,他的无数个子类就会随之改变,从而造成难以预知的错误。
桥接模式
将抽象部分与它的实现部分分离,使他们都可以独立地变化。实际上就是说若系统有多中分类方式,则把这些分类方式都给分离出来让他们独自变化,减少它们之间的耦合性。
package bridgemode;
public class BridgeMode {
public static void main(String[] args) {
//安卓手机玩游戏
Brand android = new AndroidPhone();
android.run(new Game());
//苹果手机看电影
Brand apple = new ApplePhone();
apple.run(new VideoPlayer());
}
}
/**
* 手机品牌
*/
interface Brand {
//每个手机都需要运行软件
void run(Software software);
}
class AndroidPhone implements Brand{
@Override
public void run(Software software) {
System.out.print("安卓手机");
software.run();
}
}
class ApplePhone implements Brand{
@Override
public void run(Software software) {
System.out.print("苹果手机");
software.run();
}
}
/**
* 不同的软件
*/
interface Software {
//不同手机软件运行效果
void run();
}
class Game implements Software{
@Override
public void run() {
System.out.println("玩游戏");
}
}
class VideoPlayer implements Software{
@Override
public void run() {
System.out.println("播放电影");
}
}
输出:
安卓手机玩游戏
苹果手机播放电影
通过组合来代替继承,我们就不需要实现特定的类(如看电影的安卓手机)。此外如果要新增一个手机品牌或者新增一个手机软件,只需要加一个实现类而不会影响其他类。这一点也能很好的反映出开放封闭原则.
注:我把手机品牌和手机软件两种分类方式看作平级,所以使用的关系是引用。书中认为手机包含了手机软件,所以关系为聚合。这点不同我觉得无伤大雅。
plantuml
@startuml
interface Brand{
{abstract}run(Software)
}
Brand <|.. AndroidPhone
class AndroidPhone{
run(Software)
}
Brand <|.. ApplePhone
class ApplePhone{
run(Software)
}
Software <-- Brand
interface Software{
{abstract}run()
}
Software <|.. Game
class Game{
run()
}
Software <|.. VideoPlayer
class VideoPlayer{
run()
}
@enduml