zoukankan      html  css  js  c++  java
  • 桥接模式(21)

    今天我们来讲一下桥接模式。

    一、案例

    我有N牌子的一个手机,需要运行一款游戏软件。咱们用简单的控制台应用程序来实现一下。

     1     /// <summary>
     2     /// N品牌的手机中的游戏
     3     /// </summary>
     4     class HandsetNGame
     5     {
     6         public void Run()
     7         {
     8             Console.WriteLine("运行N品牌手机游戏");
     9         }
    10     }

    客户端调用:

    1         public static void Main()
    2         {
    3             HandsetNGame n = new HandsetNGame();
    4             n.Run();
    5             Console.ReadKey();
    6         }

    二、演绎

    1、第一步演绎:

    如果我不仅有N品牌的手机,还有M品牌的手机也需要运行这款游戏软件,怎么办?

    我们可以将运行游戏软件抽象出一个父类,让N,M品牌的手机继承这个父类。代码如下:

     1     /// <summary>
     2     /// 父类
     3     /// </summary>
     4     class HandsetGame
     5     {
     6         public virtual void Run()
     7         {
     8 
     9         }
    10     }
    11     /// <summary>
    12     /// 子类
    13     /// </summary>
    14     class HandsetNGame : HandsetGame
    15     {
    16         public override void Run()
    17         {
    18             Console.WriteLine("运行N品牌手机游戏");
    19         }
    20     }
    21     /// <summary>
    22     /// 子类
    23     /// </summary>
    24     class HandsetMGame : HandsetGame
    25     {
    26         public override void Run()
    27         {
    28             Console.WriteLine("运行M品牌手机游戏");
    29         }
    30     }

    客户端调用:

    1         public static void Main()
    2         {
    3             HandsetGame gameM = new HandsetMGame();
    4             HandsetGame gameN = new HandsetNGame();
    5             gameM.Run();
    6             gameN.Run();
    7             Console.ReadKey();
    8         }

    2、第二步演绎

    还有一个问题是,手机不仅能玩游戏,还要有通讯录的功能,也就是说N,M品牌的手机都有通讯录的功能,那么我们该怎么做呢?

    小伙伴们说,这容易啊,跟第一步一样,抽象出一个通讯录的父类,让N,M品牌手机通讯录继承这个父类即可。代码如下:

     1     /// <summary>
     2     /// 父类
     3     /// </summary>
     4     class HandsetBrand
     5     {
     6         public virtual void Run()
     7         {
     8             
     9         }
    10     }
    11     /// <summary>
    12     /// 子类
    13     /// </summary>
    14     class HandNsetBrand:HandsetBrand
    15     {
    16         public override void Run()
    17         {
    18             Console.WriteLine("运行N品牌手机通讯录");
    19         }
    20     }
    21     /// <summary>
    22     /// 子类
    23     /// </summary>
    24     class HandMsetBrand:HandsetBrand
    25     {
    26         public override void Run()
    27         {
    28             Console.WriteLine("运行M品牌手机通讯录");
    29         }
    30     }

    客户端调用是类似的,在此不写代码了。

    好,那么问题来了,我们手机不仅有游戏,通讯录功能,还有很多很多的功能,例如,照相,彩信等等等等,照这样下去,我们这种设计将会让类异常的多,项目异常的庞大,出现这种情况,我们不禁怀疑我们的这种设计是不是出现了问题,如果出现了问题,我们改用什么方式解决呢?

    对象的继承关系是在编译时就定义好了,所以无法在运行时改变从父类继承的实现,子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或者被其他更适合的类替换,这种依赖关系限制了灵活性并最终限制了复用性。

    在这里首先我们来讲一个设计原则,合成/聚合复用原则。

    合成/聚合复用原则,尽量使用合成/聚合尽量不要使用类继承,聚合表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成表示一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。打个比方,大雁有两只翅膀,翅膀和大雁就是部分和整体的关系,他们的生命周期是相同的,是合成。大雁和雁群,是聚合关系。

    好,本着合成/聚合复用原则,我们重新来设计一下我们案例中的代码

     1     //手机软件
     2     abstract class HandsetSoft
     3     {
     4         public abstract void Run();
     5     }
     6     //手机游戏
     7     class HandsetGame:HandsetSoft
     8     {
     9         public override void Run()
    10         {
    11             Console.WriteLine("运行手机游戏");
    12         }
    13     }
    14     //手机通讯录
    15     class HandsetAddressList:HandsetSoft
    16     {
    17         public override void Run()
    18         {
    19             Console.WriteLine("运行手机通讯录");
    20         }
    21     }
    22     //手机品牌
    23     abstract class HandsetBrand
    24     {
    25         protected HandsetSoft soft;
    26         //品牌需要关注软件,所以可在机器中安装软件,以备运行
    27         public void SetHandsetSoft(HandsetSoft soft)
    28         {
    29             this.soft = soft;
    30         }
    31         //运行
    32         public abstract void Run();
    33     }
    34     //具体的手机品牌N
    35     class HandsetBrandN:HandsetBrand
    36     {
    37         public override void Run()
    38         {
    39             soft.Run();
    40         }
    41     }
    42     //具体的手机品牌M
    43     class HandsetBrandM:HandsetBrand
    44     {
    45         public override void Run()
    46         {
    47             soft.Run();
    48         }
    49     }

    客户端调用:

     1         public static void Main()
     2         {
     3             HandsetBrand ab;
     4             ab = new HandsetBrandN();
     5             ab.SetHandsetSoft(new HandsetGame());
     6             ab.Run();
     7 
     8             ab.SetHandsetSoft(new HandsetAddressList());
     9             ab.Run();
    10 
    11             ab = new HandsetBrandM();
    12             ab.SetHandsetSoft(new HandsetGame());
    13             ab.Run();
    14 
    15             ab.SetHandsetSoft(new HandsetAddressList());
    16             ab.Run();
    17             Console.ReadKey();
    18         }

    以上就是我们要将的一种设计模式:桥接模式。

    桥接模式,将抽象部分与他的实现部分分离,使他们都可以独立的变化。

    这里解释一下:什么叫抽象与他的实现分离?这并不是说,让抽象类与其派生类分离,因为这没有任何的意义,实现指的是抽象类和他的派生类用来实现自己的对象。

    真正的理解的设计原则,很多的设计模式其实就是设计原则的使用,在不知不觉中,我们就使用了很多的设计模式了。

    今天的桥接模式我们先讲到这里,下一篇我们讲 命令模式


     本系列将持续更新,喜欢的小伙伴可以点一下关注和推荐,谢谢大家的支持

  • 相关阅读:
    MySQL中数据类型介绍
    Linux rpm命令详解
    Cloudera Manager集群官方默认的各个组件开启默认顺序
    全网最全Python学习路线图+14张思维导图,让python初学者不走弯路!
    python插入Elasticsearch操作
    最全的常用正则表达式大全(校验数字,字符,号码等)
    django.db.utils.OperationalError: (1193, "Unknown system variable 'storage_engine'")
    MVC/MVT/装饰器
    ModuleNotFoundError: No module named 'tools.utils'
    request +lxml 天眼查爬虫
  • 原文地址:https://www.cnblogs.com/xiaomowang/p/6396777.html
Copyright © 2011-2022 走看看