ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

设计模式之桥接模式、合成/聚合复用原则的使用

2021-07-17 13:29:32  阅读:158  来源: 互联网

标签:SmartPhone Run 桥接 void 复用 new 手机 设计模式 class


1. 定义

桥接模式(Bridge):将抽象部分与它的实现部分分离,使它们都可以独立地变化

简单来说,就是把经常变化的类和经常变化的类分离,而不是用继承来耦合其关系,应该分离成两个类,降低耦合度

2. 题目引入

题目:有一个N品牌手机和一个M品牌手机,两个手机上都要有通讯录软件、一个游戏

背景:不同手机品牌的系统可能不一样,型号不一样,软件不可以共用,比如苹果手机游戏不能在安卓上玩,我们这取极端情况,所有软件在不同品牌手机上都不兼容

3. 一般解决方法

分析题目发现,有两种品牌手机N和M,所以我们可以抽象出一个抽象类即手机类,方便N和M的继承;但是发现软件在不同手机是不相同的,所以具体软件是不可以写在抽象手机类里的,要写在具体手机里,即结构图是:

在这里插入图片描述
每个都是继承的关系

代码:

类:

    //智能手机父类
    class SmartPhone
    {
        public virtual void Run()
        {
            Console.WriteLine("运行");
        }
    }

    //N手机品牌
    class SmartN : SmartPhone
    {

    }

    //M手机品牌
    class SmartM : SmartPhone
    {

    }

    //N手机的使用功能通讯录
    class AddressListN : SmartN
    {
        public override void Run()
        {
            Console.WriteLine("使用N手机的通讯录");
        }
    }

    //N手机的游戏
    class GameN : SmartN
    {
        public override void Run()
        {
            Console.WriteLine("玩N手机的游戏");
        }
    }


    //M手机的使用功能通讯录
    class AddressListM : SmartM
    {
        public override void Run()
        {
            Console.WriteLine("使用M手机的通讯录");
        }
    }

    //M手机的游戏
    class GameM : SmartM
    {
        public override void Run()
        {
            Console.WriteLine("玩M手机的游戏");
        }
    }

客户端:

            SmartPhone smartPhone1 = new GameN();//N品牌的游戏
            smartPhone1.Run();

            SmartPhone smartPhone2 = new AddressListN();//N品牌的通讯录
            smartPhone2.Run();

            SmartPhone smartPhone3 = new GameM();//M品牌的游戏
            smartPhone3.Run();

            SmartPhone smartPhone4 = new AddressListM();//M品牌的通讯录
            smartPhone4.Run();

这样做结果是有的,但是,耦合度太高,继承太过严谨,不能随意扩展,一扩展就要加类,比方又有一个输入法软件的功能,那么N、M手机都要再加这个输入法的类;如果有新功能(比如登录Register())还要改变父类的功能,目前父类只有一个Run()功能,还需要在父类SmartPhone加上Register()方法,导致其他的类都要变化,如果要添加则父类一定会改变,是紧耦合,违反了开放封闭原则

及其不方便

4. 合成/聚合复用原则

合成/聚合复用原则(CARP):尽量使用合成/聚合,尽量不要使用类继承。

对该原则的理解:
合成(Composition,也有翻译成组合)和聚合(Aggregation)都是关联的特殊种类。聚合表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。

比方说,大雁有两个翅膀,翅膀与大雁是部分和整体的关系,并且它们的生命周期是相同的,于是大雁和翅膀就是合成关系。而大雁是群居动物,所以每只大雁都是属于一个雁群,一个雁群可以有多只大雁,所以大雁和雁群是聚合关系。
在这里插入图片描述
优点:
合成/聚合复用原则的好处是,优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

就刚才的例子,手机是不同的品牌公司,各自做自己的软件,就像刚刚一般的设计一样,而PC却是硬件厂商做硬件,软件厂商做软件,组合起来才是可以用的机器,实际上,像‘游戏’、‘通讯录’、‘MP3音乐播放’这些功能都是软件,如果我们可以让其分离与手机的耦合,那么就可以大大减少面对新需求时改动过大的不合理情况。

解决办法其实就是应该有个‘手机品牌’抽象类和‘手机软件’抽象类,让不同的品牌和功能都分别继承于它们,这样要增加新的品牌或新的功能都不用影响其他类了。

结构图:
在这里插入图片描述
现在我们需要将两个抽象类即手机品牌和手机软件结合起来,应该是手机品牌包含有手机软件,但软件并不是品牌的一部分,所以它们之间是聚合关系

结构图:
在这里插入图片描述

代码:
类:

//手机软件的抽象类
    abstract class HandsetSoft
    {
        public abstract void Use();
    }
    //手机通讯录
    class AddressList : HandsetSoft
    {
        public override void Use()
        {
            Console.WriteLine("运行手机通讯录");
        }
    }
    //手机游戏
    class Game : HandsetSoft
    {
        public override void Use()
        {
            Console.WriteLine("运行手机游戏");
        }
    }

    //手机品牌的抽象类
    abstract class SmartPhone
    {
        //聚合手机软件进来,后面如果要改或者添加手机软件,不会对手机有影响,因为手机软件都是继承HandsetSoft,而手机抽象类里面有HandsetSoft类型的字段调用
        protected HandsetSoft soft1;
        public void SetHandsetSoft(HandsetSoft soft)
        {
           soft1= soft;
        }

        public abstract void Run();
    }
    //N手机品牌
    class SmartPhoneN : SmartPhone
    {
        public override void Run()
        {
            soft1.Use();
        }
    }
    //M手机品牌
    class SmartPhoneM : SmartPhone
    {
        public override void Run()
        {
            soft1.Use();
        }
    }

客户端:

	SmartPhone smartPhone1 = new SmartPhoneN();
    smartPhone1.SetHandsetSoft(new AddressList());
    smartPhone1.Run();

    SmartPhone smartPhone2 = new SmartPhoneN();
    smartPhone2.SetHandsetSoft(new Game());
    smartPhone2.Run();

    SmartPhone smartPhone3 = new SmartPhoneM();
    smartPhone3.SetHandsetSoft(new AddressList());
    smartPhone3.Run();

    SmartPhone smartPhone4 = new SmartPhoneM();
    smartPhone4.SetHandsetSoft(new Game());
    smartPhone4.Run();

这样就分离了两个可能不断变化的类,现在如果要增加一个功能,比如MP3音乐播放功能,那么只要增加这个类就行了,不会影响其他任何类,类的个数增加也只是一个

如果是要增加S品牌,只需要增加一个品牌子类就可以了,个数也是一个,不会影响其他类的改动

这显然是也符合了开放-封闭原则设计原则,这样的设计显然不会修改原来的代码,而只是扩展类就行了。但今天我的感受最深的是合成/聚合复用原则,也就是优先使用对象的合成或聚合,而不是类继承,聚合的魅力无限呀。相比,继承很容易造成不必要的麻烦。

5. 桥接模式使用例子

刚刚这个结构图,其实就是桥接模式了
在这里插入图片描述
中间有一条线,连接了两个抽象类,像一条桥,所以称为桥接模式

其实,上面那个合成/聚合复用原则的例子,就是桥接模式的使用

6. 桥接模式使用结构图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

7. 全部程序(例子的)

using System;

namespace 桥接模式
{
    class Program
    {
        static void Main(string[] args)
        {
            //1.按品牌分类的主程序
            //SmartPhone smartPhone1 = new GameN();//N品牌的游戏
            //smartPhone1.Run();

            //SmartPhone smartPhone2 = new AddressListN();//N品牌的通讯录
            //smartPhone2.Run();

            //SmartPhone smartPhone3 = new GameM();//M品牌的游戏
            //smartPhone3.Run();

            //SmartPhone smartPhone4 = new AddressListM();//M品牌的通讯录
            //smartPhone4.Run();

            //2.使用桥接模式的主程序

            SmartPhone smartPhone1 = new SmartPhoneN();
            smartPhone1.SetHandsetSoft(new AddressList());
            smartPhone1.Run();

            SmartPhone smartPhone2 = new SmartPhoneN();
            smartPhone2.SetHandsetSoft(new Game());
            smartPhone2.Run();

            SmartPhone smartPhone3 = new SmartPhoneM();
            smartPhone3.SetHandsetSoft(new AddressList());
            smartPhone3.Run();

            SmartPhone smartPhone4 = new SmartPhoneM();
            smartPhone4.SetHandsetSoft(new Game());
            smartPhone4.Run();

            Console.Read();
        }
    }

    //题目:有一个N品牌手机和一个M品牌手机,两个手机上都要有通讯录软件、一个游戏
    //背景:不同手机品牌的系统可能不一样,型号不一样,软件不可以共用,比如苹果手机游戏不能在安卓上玩,我们这取极端情况,所有软件在不同品牌手机上都不兼容

    //1.按品牌分类
    //提前说明下面这种方法缺点:继承太过严谨,不能随意扩展,一扩展就要加类,比方又有一个输入法软件的功能,那么N、M手机都要再加这个输入法的类;如果有新功能还要改变父类的功能,目前父类只有一个Run()功能,如果要添加则父类一定会改变,是紧耦合,违反了开放封闭原则

    //手机品牌
    //class SmartPhone
    //{
    //    public virtual void Run()
    //    {
    //        Console.WriteLine("运行");
    //    }
    //}

    //N手机品牌
    //class SmartN : SmartPhone
    //{

    //}

    //M手机品牌
    //class SmartM : SmartPhone
    //{

    //}

    //N手机的使用功能通讯录
    //class AddressListN : SmartN
    //{
    //    public override void Run()
    //    {
    //        Console.WriteLine("使用N手机的通讯录");
    //    }
    //}

    //N手机的游戏
    //class GameN : SmartN
    //{
    //    public override void Run()
    //    {
    //        Console.WriteLine("玩N手机的游戏");
    //    }
    //}


    //M手机的使用功能通讯录
    //class AddressListM : SmartM
    //{
    //    public override void Run()
    //    {
    //        Console.WriteLine("使用M手机的通讯录");
    //    }
    //}

    //M手机的游戏
    //class GameM : SmartM
    //{
    //    public override void Run()
    //    {
    //        Console.WriteLine("玩M手机的游戏");
    //    }
    //}


    //2. 按桥接模式
    //使用此模式的好处:此模式将抽象和实现分开了,即将抽象的手机品牌和实现的软件功能分开了,让其各自变化,方便了后续的修改,修改或者添加的时候不需要改变很多,如果是第一种方法纯继承,改变的就太多了

    //手机软件的抽象类
    abstract class HandsetSoft
    {
        public abstract void Use();
    }
    //手机通讯录
    class AddressList : HandsetSoft
    {
        public override void Use()
        {
            Console.WriteLine("运行手机通讯录");
        }
    }
    //手机游戏
    class Game : HandsetSoft
    {
        public override void Use()
        {
            Console.WriteLine("运行手机游戏");
        }
    }




    //手机品牌的抽象类
    abstract class SmartPhone
    {
        //聚合手机软件进来,后面如果要改或者添加手机软件,不会对手机有影响,因为手机软件都是继承HandsetSoft,而手机抽象类里面有HandsetSoft类型的字段调用
        protected HandsetSoft soft1;
        public void SetHandsetSoft(HandsetSoft soft)
        {
           soft1= soft;
        }

        public abstract void Run();
    }
    //N手机品牌
    class SmartPhoneN : SmartPhone
    {
        public override void Run()
        {
            soft1.Use();
        }
    }
    //M手机品牌
    class SmartPhoneM : SmartPhone
    {
        public override void Run()
        {
            soft1.Use();
        }
    }
}

8. 总结

桥接模式所说的‘将抽象部分与它的实现部分分离’,还是不好理解,我的理解就是实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。

也就是说,在发现我们需要多角度去分类实现对象,而只用继承会造成大量的类增加,不能满足开放-封闭原则时,就应该要考虑用桥接模式了。

标签:SmartPhone,Run,桥接,void,复用,new,手机,设计模式,class
来源: https://blog.csdn.net/weixin_50233101/article/details/118769167

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有