zoukankan      html  css  js  c++  java
  • 《深入浅出设计模式中文版》读书笔记(二)

    接着上回的“针对接口编程,不针对实现编程”原则说。

      假设开始我们设计了一个鸭子基类,发声、游泳和显示外观是鸭子类的功能。发声和游泳是通用的方法,放在基类实现,显示外观由于鸭子的不同而不同,放在继承类实现。可以设计出下面的类结构。

    public abstract class DuckBase
        {
            
    public virtual void Quack()
            {
                Console.WriteLine(
    "我会嘎嘎叫");
            }
            
    public virtual void Swin()
            {
                Console.WriteLine(
    "我会游泳");
            }
            
    public abstract void Display();

        }
        public class GreenHeadDuck : DuckBase
        {
            
    public override void Display()
            {
                Console.WriteLine(
    "我是绿头鸭子");
            }
        }
        
    public class RedHeadDuck : DuckBase
        {
            
    public override void Display()
            {
                Console.WriteLine(
    "我是红头鸭子");
            }
        }

    看起来还是比较OO的,利用了继承,复用了游泳和发声的代码。可是突然有一天需要创新,提出鸭子也应该可以飞,有人就提出来“这还不简单,在基类上加一个方法,鸭子都会飞了”。

    public abstract class DuckBase
        {
            
    public virtual void Quack()
            {
                Console.WriteLine(
    "我会嘎嘎叫");
            }
            
    public virtual void Swin()
            {
                Console.WriteLine(
    "我会游泳");
            }
            
    public abstract void Display();

            public virtual void Fly()
            {
                Console.WriteLine(
    "我是鸭子,我会飞");
            }
        }

    貌似解决了鸭子会飞的问题。可是有一天问题来了,橡皮鸭子怎么会飞呢?它没有生命啊?

      怎么回事呢?就是由于我们在基类添加的实现方法,继承类继承了这个方法,所有各种类型的鸭子都会飞了。可是还是有一些是不应该飞的,就像橡皮鸭子,木头鸭子等等。看来为了复用“飞”,而使用继承,结局并不理想。

      当然了,也有解决的办法。既然是基类的方法,我们可以在继承类中重写它,如果不会飞就什么都不做?发声也出现了类似问题,不是每种鸭子都“嘎嘎”叫的,有的是“吱吱”发声,好吧,重写发声方法,以前的呢?都检查一遍,以后的新类型鸭子也需要注意叫声和是否会飞,好像有点麻烦?

    public class RubberDuck : DuckBase
        {
            
    public override void Display()
            {
                Console.WriteLine(
    "我是橡皮鸭子");
            }
            
    public override void Quack()
            {
                Console.WriteLine(
    "我会咯吱咯吱叫");
            }
            
    public override void Fly()
            {
                Console.WriteLine(
    "我不会飞");
            }
        }

    这样的继承造成了以下几个问题:

    •   鸭子不能同时飞和发声。
    •   改变牵一发,而动全身,其他类型的鸭子平白无故的多了一些不需要的功能。如果确实不要还要修改其他类型鸭子的代码。

      有人又提出来了,利用接口吧。将会飞和发声写两个接口,然鸭子类去实现,实现了接口的就具有了这两个功能,没有实现的就不受影响。

    public abstract class DuckBase
        {

            public virtual void Swin()
            {
                Console.WriteLine(
    "我会游泳");
            }
            
    public abstract void Display();

          
        }
        public class GreenHeadDuck : DuckBase,IFlyBehavior,IQuackBehavior
        {
            
    public override void Display()
            {
                Console.WriteLine(
    "我是绿头鸭子");
            }

            public void Fly()
            {
                
    throw new NotImplementedException();
            }

            public void Quack()
            {
                
    throw new NotImplementedException();
            }
        }
        
    public class RedHeadDuck : DuckBase, IFlyBehavior, IQuackBehavior
        {
            
    public override void Display()
            {
                Console.WriteLine(
    "我是红头鸭子");
            }

            public void Quack()
            {
                
    throw new NotImplementedException();
            }

            public void Fly()
            {
                
    throw new NotImplementedException();
            }
        }
        
    public class RubberDuck : DuckBase
        {
            
    public override void Display()
            {
                Console.WriteLine(
    "我是橡皮鸭子");
            }
            
    public override void Quack()
            {
                Console.WriteLine(
    "我会咯吱咯吱叫");
            }
              }

        public interface IFlyBehavior
        {
            
    void Fly();
        }
       
      
        
    public interface IQuackBehavior
        {
            
    void Quack();
        }

    这样还是有点问题。就是很是很多的鸭子都会飞的,都会叫的,这部分的鸭子的飞和叫的代码就会重复,而且以前现有鸭子都需要检查一遍,都可能需要修改。接口虽然解决了只有继承才会具有相应的功能,但是因为接口没有实现,只有方法体的定义,所以造成了代码没有得到复用。表面看起来这两者好像是矛盾的。

      引出另外一个设计原则:

      找出应用中可能需要变化的地方,把他们独立出来,不要和那些不需要变化的代码混合在一起。把会变化的代码“封装”起来,好让修改的时候不影响其他部分。使得代码的修改对系统的影响最小,使得系统更具有弹性。

      也就是说每次有新的需求,都会造成一部分代码的变化,将这部分变化的代码抽象出来,保持其他代码的稳定性。让系统的某部分修改不影响其他部分,这样其他部分就不需要测试,否则就需要全部测试一遍才可以保证代码的修改是正确的。

      上面这个列子中的飞和叫是变化的地方,其他的没有太大变化。飞也有很多种方式,叫也有很多种“声音”。这两种叫做“行为”更好。原来的鸭子类,这两种行为是写死的,能否在初始化鸭子的时候,自己指定行为的方式呢?可以,开放一个属性,或者是一个方法,可以设置定义在鸭子基类中的行为就可以了,或者在构造函数中添加参数,指定行为。

    代码

     

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace BeautyCode.DesignPattern.Head.First
    {
        public abstract class DuckBase1
        {
            private IFlyBehavior1 _flyBehavior;

            public IFlyBehavior1 FlyBehavior
            {
                get { return _flyBehavior; }
                set { _flyBehavior = value; }
            }
            private IQuackBehavior1 _quackBehavior;

            public IQuackBehavior1 QuackBehavior
            {
                get { return _quackBehavior; }
                set { _quackBehavior = value; }
            }
            public virtual void Swin()
            {
                Console.WriteLine("我会游泳");
            }
            public void PerformQuack()
            {
                _quackBehavior.Quack();
            }
            public void PerformFly()
            {
                _flyBehavior.Fly();
            }
            
            public abstract void Display();
        }
        public class GreenHeadDuck1 : DuckBase1
        {
            public override void Display()
            {
                Console.WriteLine("我是绿头鸭子");
            }
        }
        public class RedHeadDuck1 : DuckBase1
        {
            public override void Display()
            {
                Console.WriteLine("我是红头鸭子");
            }
        }
        public class RubberDuck1 : DuckBase1
        {
            public override void Display()
            {
                Console.WriteLine("我是橡皮鸭子");
            }
           
        }
        public interface IFlyBehavior1
        {
              void Fly();
        }
        public class FlyWithWings : IFlyBehavior1
        {
            public void Fly()
            {
                Console.WriteLine("有翅膀飞了");
            }
        }
        public class FlyNoWay : IFlyBehavior1
        {
            public void Fly()
            {
                Console.WriteLine("没有也飞了");
            }
        }
        public interface IQuackBehavior1
        {
              void Quack();
        }
        public class QuackA : IQuackBehavior1
        {
            public void Quack()
            {
                Console.WriteLine("嘎嘎");
            }
        }
        public class Squeak : IQuackBehavior1
        {
            public void Quack()
            {
                Console.WriteLine("吱吱");
            }
        }
        public class MuteQuack : IQuackBehavior1
        {
            public void Quack()
            {
                Console.WriteLine("哇哇");
            }
        }

    }

    调用代码
     Head.First.DuckBase1 duck = new Head.First.GreenHeadDuck1();
                duck.FlyBehavior 
    = new Head.First.FlyWithWings();
                duck.PerformFly();

    【Blog】http://virusswb.cnblogs.com/

    【MSN】jorden008@hotmail.com

    【说明】转载请标明出处,谢谢

  • 相关阅读:
    js Math对象
    extjs 获取Dom对象
    easyui validatebox 验证集合
    Ext.Ajax.request与form.submit的用法区别
    js获取url参数值
    【原创】extjs4做的grid,带分页,搜索
    SqlServer2005数据库同步
    【原创】jquery实现动态多组图片切换
    easyui表单数据验证
    对象模型图【OMD】阅读指南
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/1769671.html
Copyright © 2011-2022 走看看