设计模式学习(一):多用组合少用继承(C#)

简介: 《深入浅出设计模式》学习笔记第一章 原始需求和设计 事情是这样开始的,公司需要做一套程序,鸭子,设计如下: 一个鸭子父类,多个派生类,三个可override的方法。 第一次需求变更 我们要会飞的鸭子!!!!! 所以我们做了如下的更改: 父类加了fly方法,嗯,所有的鸭子都会飞了...

《深入浅出设计模式》学习笔记第一章

原始需求和设计

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

一个鸭子父类,多个派生类,三个可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();
        }
    }
}

需求完成!!!

最终结构如下:

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

下面是我的关于ASP.NET Core Web API相关技术的公众号--草根专栏:

目录
相关文章
|
2天前
|
Java 物联网 C#
C#/.NET/.NET Core学习路线集合,学习不迷路!
C#/.NET/.NET Core学习路线集合,学习不迷路!
|
29天前
|
设计模式 安全 Java
C# 一分钟浅谈:设计模式之单例模式
【10月更文挑战第9天】单例模式是软件开发中最常用的设计模式之一,旨在确保一个类只有一个实例,并提供一个全局访问点。本文介绍了单例模式的基本概念、实现方式(包括饿汉式、懒汉式和使用 `Lazy<T>` 的方法)、常见问题(如多线程和序列化问题)及其解决方案,并通过代码示例详细说明了这些内容。希望本文能帮助你在实际开发中更好地应用单例模式,提高代码质量和可维护性。
29 1
|
1月前
|
开发框架 缓存 算法
开源且实用的C#/.NET编程技巧练习宝库(学习,工作,实践干货)
开源且实用的C#/.NET编程技巧练习宝库(学习,工作,实践干货)
|
2月前
|
Linux C# 开发者
Uno Platform 驱动的跨平台应用开发:从零开始的全方位资源指南与定制化学习路径规划,助您轻松上手并精通 C# 与 XAML 编程技巧,打造高效多端一致用户体验的移动与桌面应用程序
【9月更文挑战第8天】Uno Platform 的社区资源与学习路径推荐旨在为初学者和开发者提供全面指南,涵盖官方文档、GitHub 仓库及社区支持,助您掌握使用 C# 和 XAML 创建跨平台原生 UI 的技能。从官网入门教程到进阶技巧,再到活跃社区如 Discord,本指南带领您逐步深入了解 Uno Platform,并提供实用示例代码,帮助您在 Windows、iOS、Android、macOS、Linux 和 WebAssembly 等平台上高效开发。建议先熟悉 C# 和 XAML 基础,然后实践官方教程,研究 GitHub 示例项目,并积极参与社区讨论,不断提升技能。
83 2
|
2月前
|
设计模式 C# 开发者
C#设计模式入门实战教程
C#设计模式入门实战教程
|
3月前
|
设计模式 算法 C#
C#设计模式之策略模式
C#设计模式之策略模式
84 19
|
3月前
|
设计模式 安全 程序员
C#设计模式之单例模式
C#设计模式之单例模式
48 3
|
3月前
|
开发框架 .NET API
C#/.NET/.NET Core推荐学习书籍(24年8月更新)
C#/.NET/.NET Core推荐学习书籍(24年8月更新)
|
5月前
|
设计模式 存储 算法
设计模式学习心得之五种创建者模式(2)
设计模式学习心得之五种创建者模式(2)
45 2
|
5月前
|
设计模式 uml
设计模式学习心得之前置知识 UML图看法与六大原则(下)
设计模式学习心得之前置知识 UML图看法与六大原则(下)
40 2

热门文章

最新文章

  • 1
    C++一分钟之-设计模式:工厂模式与抽象工厂
    42
  • 2
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    46
  • 3
    C++一分钟之-C++中的设计模式:单例模式
    53
  • 4
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    37
  • 5
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    61
  • 6
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    56
  • 7
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    40
  • 8
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    49
  • 9
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    105
  • 10
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    75