原始需求和设计

事情是这样开始的,公司需要做一套程序,鸭子,设计如下:

一个鸭子父类,多个派生类,三个可override的方法。

第一次需求变更

我们要会飞的鸭子!!!!!

所以我们做了如下的更改:

父类加了fly方法,嗯,所有的鸭子都会飞了,需求实现!

问题发生了,因为不是所有的鸭子都会飞

我们可以在派生类中把父类的fly方法中的内容覆盖掉,那么这个鸭子就不会飞了!

那么问题又来了,如果再出现几个新型鸭子都不会飞,是不是每个都得覆盖一遍fly方法啊????

也许,可以用接口?

把每个方法都做成接口,如图:

这是超笨的方法,如果一些鸭子的飞行方式发生变化,那么得改多少个类啊。。。

现在的情况是:

继承不行,因为鸭子的行为(需求)在子类里面不断变化,而使用接口又无法进行复用。

幸好,面向对象软件开发有这样一个原则:

找出应用中可能需要变化的地方,把它们独立起来,不要和那些不需要发生变化的代码混在一起。

这句话另一种思考方式就是:把变化的部分取出并封装起来,以便以后可以轻松的改动或扩展,而不影响其他部分

所以我们应该把鸭子的行为都提取出来。

根据需求,我们知道鸭子的fly和quack行为经常发生变化,所以我们现在的设计是这样的:

设计原则:

针对接口编程而不是针对实现编程。

这是变化的部分,对于Fly和Quack分别定义接口。

namespace DesignPatterns.Intro.Bases
{
    public interface IFlyBehavior
    {
        void Fly();
    }
}

namespace DesignPatterns.Intro.Bases
{
    public interface IQuackBehavior
    {
        void Quack();
    }
}

然后实现几种类型的Fly和Quack:

namespace DesignPatterns.Intro.Derives
{
    public class Squeak: IQuackBehavior
    {
        public void Quack()
        {
            Console.WriteLine("吱吱");
        }
    }
}

namespace DesignPatterns.Intro.Derives
{
    public class NormalQuack: IQuackBehavior
    {
        public void Quack()
        {
            Console.WriteLine("呱呱");
        }
    }
}

namespace DesignPatterns.Intro.Derives
{
    public class MuteQuack: IQuackBehavior
    {
        public void Quack()
        {
            Console.WriteLine("---------");
        }
    }
}

整合鸭子的行为

让我们来定义鸭子:

namespace ConsoleApp2.Bases
{
    public abstract class Duck
    {
        private readonly IFlyBehavior _flyBehavior;
        private readonly IQuackBehavior _quackBehavior;

        protected Duck(IFlyBehavior flyBehavior = null, IQuackBehavior quackBehavior = null)
        {
            _flyBehavior = flyBehavior ?? new FlyNoWay();
            _quackBehavior = quackBehavior ?? new MuteQuack();
        }

        public abstract void Display();

        public void PerformFly()
        {
            _flyBehavior.Fly();
        }

        public void PerformQuack()
        {
            _quackBehavior.Quack();
        }

        public void Swim()
        {
            Console.WriteLine("所有的鸭子都会游泳");
        }
    }
}

这是鸭子的抽象类。

建立实际的鸭子:

namespace ConsoleApp2.Derives
{
    public class MallardDuck: Duck
    {
        public MallardDuck(IFlyBehavior flyBehavior = null, IQuackBehavior quackBehavior = null) : base(flyBehavior, quackBehavior)
        {
        }

        public override void Display()
        {
            Console.WriteLine("我是个野鸭...");
        }
    }
}

测试鸭子

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            var duck = new MallardDuck(new FlyNoWay(), new NormalQuack());
            duck.PerformFly();
            duck.PerformQuack();
            duck.Display();
            Console.ReadLine();
        }
    }
}

这时,需求终于完成了!

我们的鸭子根据传入的Fly和Quack实现类不同而具有不同的效果!

需求又变了,要求鸭子的行为可以随时改变

这时,我们需要动态设定行为,我们只需要加入Set方法即可:

Duck最新的代码是:

namespace ConsoleApp2.Bases
{
    public abstract class Duck
    {
        public IFlyBehavior FlyBehavior { private get; set; }
        public IQuackBehavior QuackBehavior { private get; set; }

        protected Duck(IFlyBehavior flyBehavior = null, IQuackBehavior quackBehavior = null)
        {
            FlyBehavior = flyBehavior ?? new FlyNoWay();
            QuackBehavior = quackBehavior ?? new MuteQuack();
        }

        public abstract void Display();

        public void PerformFly()
        {
            FlyBehavior.Fly();
        }

        public void PerformQuack()
        {
            QuackBehavior.Quack();
        }

        public void Swim()
        {
            Console.WriteLine("所有的鸭子都会游泳");
        }
    }
}

测试效果:

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            var duck = new MallardDuck();
            duck.PerformFly();
            duck.PerformQuack();
            duck.Display();
            duck.FlyBehavior = new FlyWithWings();
            duck.QuackBehavior = new Squeak();
            duck.PerformFly();
            duck.PerformQuack();
            Console.ReadLine();
        }
    }
}

需求完成!!!

最终结构如下:

设计原则:多用组合,少用继承

《深入浅出设计模式》读书笔记 C#版(第一章)的更多相关文章

  1. 读书笔记http之第一章

    http TCP/IP协议各层: 应用层 决定了向用户提供应用服务时通信的活动. 比如 : FTP(FileTransferProtocol,文件传输协议)和DNS(DomainNameSystem, ...

  2. 深入浅出Nodejs读书笔记

    深入浅出Nodejs读书笔记 转:http://tw93.github.io/2015-03-01/shen-ru-qian-chu-nodejs-reading-mind-map.html cate ...

  3. SQL Server2012 T-SQL基础教程--读书笔记(8 - 10章)

    SQL Server2012 T-SQL基础教程--读书笔记(8 - 10章) 示例数据库:点我 CHAPTER 08 数据修改 8.1 插入数据 8.1.1 INSERT VALUES 语句 8.1 ...

  4. 《Android开发艺术探索》读书笔记 (3) 第3章 View的事件体系

    本节和<Android群英传>中的第五章Scroll分析有关系,建议先阅读该章的总结 第3章 View的事件体系 3.1 View基本知识 (1)view的层次结构:ViewGroup也是 ...

  5. 《Android开发艺术探索》读书笔记 (13) 第13章 综合技术、第14章 JNI和NDK编程、第15章 Android性能优化

    第13章 综合技术 13.1 使用CrashHandler来获取应用的Crash信息 (1)应用发生Crash在所难免,但是如何采集crash信息以供后续开发处理这类问题呢?利用Thread类的set ...

  6. HeadFirst设计模式读书笔记--目录

    HeadFirst设计模式读书笔记(1)-策略模式(Strategy Pattern) HeadFirst设计模式读书笔记(2)-观察者模式(Observer Pattern) HeadFirst设计 ...

  7. 高性能MySQL(第4版) 第一章 MySQL架构 读书笔记

    这本书去年11月出的,今年中文版也出了,并且直接上了微信读书,之后有空就读一读,分享下读书笔记~ 原文内容比较充实,建议有时间可以读一下原文. 第一章主要是个概览. MySQL的逻辑架构 默认情况下, ...

  8. Java 线程第三版 第一章Thread导论、 第二章Thread的创建与管理读书笔记

    第一章 Thread导论 为何要用Thread ? 非堵塞I/O      I/O多路技术      轮询(polling)      信号 警告(Alarm)和定时器(Timer) 独立的任务(Ta ...

  9. Java 螺纹第三版 第一章Thread介绍、 第二章Thread创建和管理学习笔记

    第一章 Thread导论 为何要用Thread ? 非堵塞I/O      I/O多路技术      轮询(polling)      信号 警告(Alarm)和定时器(Timer) 独立的任务(Ta ...

  10. 《Android开发艺术探索》读书笔记 (9) 第9章 四大组件的工作过程

    第9章 四大组件的工作过程 9.1 四大组件的运行状态 (1)四大组件中只有BroadcastReceiver既可以在AndroidManifest文件中注册,也可以在代码中注册,其他三个组件都必须在 ...

随机推荐

  1. Redis[三] @Hash 哈希

    Redis的哈希值是字符串字段和字符串值之间的映射,所以他们是表示对象的完美数据类型 在Redis中的哈希值,可存储超过400十亿键值对. redis 提供了2套操纵 一种是批量 一种是非批量 假设需 ...

  2. 通过css控制超链接不显示下划线

    “页面属性”——“链接”——“下划线样式”——“始终无下划线” <style type="text/css"> a:link { text-decoration: no ...

  3. 常用的十大Python开发工具

    据权威机构统计,Python人才需求量每日高达5000+,但目前市场上会 Python 的程序员少之又少, 竞争小,很容易快速高薪就业.可能你并不太了解常用的十大Python开发工具都有哪些,现在告诉 ...

  4. Kafka中操作topic时 Error:Failed to parse the broker info from zookeeper

      Kafka中操作topic时 Error: Failed to parse the broker info from zookeeper 1.问题描述   2.问题原因     kafka在启动后 ...

  5. iOS 环信集成单聊界面,出现消息重复问题

    解决办法很简单,数据重复就是EaseMessageViewController和ChatViewController重复调用了这个吧?//通过会话管理者获取收发消息 [self tableViewDi ...

  6. JS跨域请求 JSONP B/S全代码

    Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面.动态网页.web服务.WCF,只要是跨域请求,一律不准:Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有 ...

  7. 配置VNC SERVER 远程访问

    1.安装软件包 # yum install tigervnc-server -y 2. 配置VNC用户 # vim /etc/sysconfig/vncservers VNCSERVERS=" ...

  8. java简单数据类型转化

    java简单数据类型,有低级到高级为:(byte,short,char)→int→long→float→double (boolean不参与运算转化) 转化可以分为 低级到高级的自动转化 高级到低级的 ...

  9. 关于在git添加远程地址的过程中遇到的问题

    问题产生的过程 我根据菜鸟教程的步骤,做了如下操作: 1.打开安装文件夹中的git-bash程序 2.设置username和email 3.添加远程地址 结果如下: 之后通过百度知道要先git ini ...

  10. 201521123081《Java程序设计》 第3周学习总结

    1. 本周学习总结 初学面向对象,会学习到很多碎片化的概念与知识.尝试学会使用思维导图将这些碎片化的概念.知识组织起来.请使用纸笔或者下面的工具画出本周学习到的知识点.截图或者拍照上传. 参考资料:百 ...