美文网首页
《深入浅出设计模式》

《深入浅出设计模式》

作者: 曹元_ | 来源:发表于2021-04-13 07:17 被阅读0次

一、开篇乱弹

oreilly的《Head.First设计模式》,《深入浅出设计模式》是一本好书,用生动的语言讲解设计模式。而且是逐级深入,并没有一上来就gof23,就一大套设计模式的理论。而是用例子切入,使得读者逐步的理解设计模式的好处,理解设计模式可以解决的问题,使我们有了继续读下去的理由和勇气,而不至于被大片的理论所击倒。

更加容易入门,为以后读那些理论打下坚实的基础。

感谢作者写出一本这么好的书。

设计原则一:

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

把会变化的部分取出来“封装”起来,以便以后可以轻易的修改和扩展此部分,而不会影响不需要变化的部分。

单一职责原则。

设计原则二:

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

针对接口编程,这里的接口是广义的接口,不止包括语法中的接口

interface

public interface IFlyBehavior
   {
       void Fly ();
   }

还包括抽象类,其实就是一个超类型,反正不是具体的实现类,这样就可以利用多态,不会被绑死到一种具体的类型和实现上。这句话也可以解释为变量的声明应该是超类型,通常是一个抽象类或者是一个接口,因此只要是实现了超类的具体类产生的对象,都可以赋值给这个变量。

public abstract class Animal
    {
        public abstract void MakeSound();
    }
    public class Dog : Animal
    {
        public override void MakeSound()
        {
            bark();
        }
        public void bark()
        { }
    }
    public class Cat : Animal
    {
        public override void MakeSound()
        {
            meow();
        }
        public void meow()
        { }
    }

针对实现编程的调用代码

 public void Caller1()
        {
            Dog dog = new Dog();
            dog.bark();
        }

针对接口编程的调用代码

 public void Caller2()
        {
            Animal a = new Cat();
            a.MakeSound();
        }

在上面的代码中Animal a = new Cat();动物的创建还是被硬编码了,我们还可以在代码运行的时候再来指定创建的类型,就是我们不关心创建的类型是什么,只要可以进行makesound就可以了。方法有很多,常见的就是利用一个工厂类,将对象的创建交给工程类来完成,只要告诉它需要创建的对象的特征即可。这里我们简单的使用对象的类型名称代表对象。

简单工厂类

public class AnimalFactory
    {
        public static Animal CreateAnimal(string type)
        {
            switch (type)
            {
                case "dog":
                    return new Dog();
                    break;
                case "cat":
                    return new Cat();
                    break;
                default:
                    return null;
                    break;
            }
        }
    }

调用方

 public void Caller3()
        {
            Animal a = AnimalFactory .CreateAnimal ("cat");
            a.MakeSound();
        }

但是我们还是在工厂方法中看到了switch或者是很多的if。。。else。。。,如果有新类型添加,这个静态方法还是要不断的添加,越来越长,就会不符合OPC原则,有什么更好的办法吗?

答案是:有。

我们可以将系统现有的类型都预先创建出来,然后再外部调用静态方法的时候,给他需要的对象就可以了。

 public class AnimalFactory
    {
        private Dictionary<string, Animal> _animals;
        private AnimalFactory()
        {
            _animals = new Dictionary<string, Animal>();
            _animals.Add("dog", new Dog());
            _animals.Add("cat", new Cat());
        }
        private static AnimalFactory _instance = new AnimalFactory();
        private static  object _lockObj = new object();
        public static AnimalFactory GetInstance()
        {
            if (_instance == null)
            {
                lock (_lockObj)
                {
                    if (_instance == null)
                        _instance = new AnimalFactory();
                }
            }
            return _instance;
        }

        public  Animal CreateAnimal(string type)
        {
            return _animals[type];
            //switch (type)
            //{
            //    case "dog":
            //        return new Dog();
            //        break;
            //    case "cat":
            //        return new Cat();
            //        break;
            //    default:
            //        return null;
            //        break;
            //}          }
    }

调用方代码

 public void Caller3()
        {
            Animal a = AnimalFactory.GetInstance () .CreateAnimal ("cat");
            a.MakeSound();
        }

我们先创建一个字典,作为容器,预先放置了全部的类型及其类型名称,然后再需要的时候取一个出来就可以了。如果有新类型添加,也不用每次都添加一个if或者是case了,只要修改工厂方法的静态构造函数,在字典中添加新元素就可以了。

这时候有些人会觉得这里好像很眼熟,对了,就是容器,IOC容器,依赖注入,我们不知不觉的重构到了IOC容器。IOC容器就是一个类似字典的东西,使得你事先可以注册很多的类型在里面,设置好这些类型的映射关系,配置好容器之后,容器中就会有很多的类型实例,然后再用容器的Resolv方法来获取指定类型的实例,而不用new方法来创建了。

而且上面最后的工厂类,我们还使用了单件模式以及简单工厂模式。

类型的注册可以采用代码编写,就好像向字典中添加元素一样。也可以采用xml配置文件,更加灵活。

二、策略模式

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

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


 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();

如果增加新的飞行方式,也只需要添加一个飞接口的实现,然后调用代码的duck.FlyBehavior = new Head.First.FlyWithWings();这一句进行修改就可以了,指定到新实现上面,具有了新的飞行方式。

三、继承与组合

经过上一次的改造,鸭子类不是靠继承行为接口而拥有行为,而是靠调用者指定行为的方式。例如:指定叫声的形式和飞的形式。保留了灵活性,将权利交给调用者,鸭子本身不再负责行为的初始化。具体的行为表现交给指定的行为方式处理。如果需要有新的行为方式定义,只要实现行为的接口,然后给鸭子指定新定义的行为方式即可。

就像上一回的例子中,将几个类结合起来使用,这就是组合composition。这种做法和“继承”不同的地方就是,鸭子的行为不是继承来的,而是和适当的行为对象“组合”来的。

这就是第三个设计原则:

多用组合,少用继承。
  使用组合使系统具有很大的弹性,可以在运行的时候动态指定行为,只要行为是符合标准的接口即可。当然,组合也不是万能的,没有银弹,在后面会讲到它的优缺点。

这种设计使得行为成为一个族群,一个算法群,可以使用里面的任何一个实现来替换。

恭喜你,你已经在使用设计模式了。也就是策略模式(Strategy Pattern),不用怀疑,你正在使用策略模式改进你的代码。

策略模式:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

关于继承和组合的一点个人想法:

我认为继承适合用在继承属性上面,例如外观、长相、显示、拥有的物品之类的。如果是行为之类的,就不适合用继承了,适合使用组合。因为继承会使得所有继承类都拥有了相同的行为方式,可能会不是想要的结果。使用组合可以灵活的定义行为、扩展行为,把行为的权力交给了初始化对象的调用者,调用者可以自己选择或者是扩展行为的方式。

设计谜题

内容:一个游戏场景,游戏中各种角色,还有各种武器。角色可以使用武器,每次只能使用一个武器,但是在游戏的过程中可以更换武器。


usingSystem; 
usingSystem.Collections.Generic; 
usingSystem.Linq; usingSystem.Text; namespace BeautyCode.DesignPattern.Head.First
{
    public interface IWeaponBehavior
    {
        void UseWeapon();
    }
    public class KnifeBehavior : IWeaponBehavior
    {
        public void UseWeapon()
        {
            throw new NotImplementedException();
        }
    }
    public class BowAndArrowBehavior : IWeaponBehavior
    {
        public void UseWeapon()
        {
            throw new NotImplementedException();
        }
    }
    public class AxeBehavior : IWeaponBehavior
    {
        public void UseWeapon()
        {
            throw new NotImplementedException();
        }
    }
    public class Sword : IWeaponBehavior
    {
        public void UseWeapon()
        {
            throw new NotImplementedException();
        }
    }
    public abstract class Character
    {
        private IWeaponBehavior _weapon;

        public IWeaponBehavior Weapon
        {
            get { return _weapon; }
            set { _weapon = value; }
        }
        public void Fight()
        {
            _weapon.UseWeapon();
        }
    }
    public class King:Character 
    {

    }
    public class Queen : Character
    {
    }
    public class Knight : Character
    {
    }
    public classTroll : Character
    {
    }
} 

调用代码

Head.First.King king = new Head.First.King();
            king.Weapon = new Head.First.Sword();
            king.Fight();

这个例子也是对前面的一个练习和巩固。关于对象的创建,可以重构为在“一、开篇乱弹”中讲解的简单工厂来实现。

如何使用设计模式

设计模式不会直接进入你的代码中,而是先进入你的大脑。首先要对设计模式有一个概念的理解,关于模式的原理,可以解决的问题,应用的场景,模式的代码结构等等。然后才能在设计新模块的时候,合适的引入模式,并且在以前的代码变得一团糟而没有弹性的时候,可以重构它们,重构到模式。

良好的OO需要具备:可复用性、可扩充性、可维护性。

模式不是代码,而是针对设计问题的通用解决方案。你可以把他们应用到特定的应用中。

我们通常会把系统中变化的部分抽出来封装。

OO基础

  • 抽象
  • 封装
  • 多态
  • 继承

OO原则

  • 封装变化
  • 多用组合,少用继承
  • 针对接口编程,不针对实现编程

OO模式

  • 策略模式-定义算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

四、观察者模式

今天要接触的是观察者模式Observer。

提到观察者,肯定会想到还应该有被观察者,也就是观察的内容,或者说是观察的主题。

首先让我们看看报纸和杂志的订阅是怎么回事:

  • 报社的业务就是出版报纸。
  • 向一家报社订阅报纸,只要有新报纸,就会给你送过来。只要你是他们的订阅者,就一直会收到。
  • 当你不想继续看报纸的时候,可以取消订阅,他们就不会再送报纸过来。
  • 只要报社存在,就会一直有人订阅或者是取消订阅。

上面就是一个典型的观察者模式,你、我、他,这些订阅报纸的人就是观察者Observer,报社就是被观察者,观察的主题Subject。

观察者模式的定义:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态的时候,它的所有依赖者都会收到通知并自动更新。

观察者有很多的实现方式,最常见的是包含Subject与Observer接口的类设计。

image

上图是我从书中摘取出来的。

  • 当两个对象之间松耦合,他们依然可以交互,但是不清楚彼此的细节。观察者模式提供了一种对象设计,实现主题和观察者之间的松耦合。

  • 对于观察者的一切,主题只是知道观察者实现了某一个接口(也就是Observer接口)。主题不需要知道观察者具体是做什么的类。

  • 任何时候我们都可以添加新的观察者,主题只是依赖于观察者列表,添加观察者也就是在列表中添加元素。同样,也可以删除观察者,也就是删除观察者列表的元素。

  • 有新类型的观察者,主题不用修改代码,只要新的观察者实现Observer接口就可以了。只需要在主题中注册观察者,然后就可以收到新通知了。

  • 二者是松耦合的,相互之间不影响,只要接口被遵守,就可以自由的改变他们。

设计原则:

为了交互对象之间的松耦合设计而努力。


usingSystem; 
usingSystem.Collections.Generic; 
usingSystem.Linq; usingSystem.Text; namespace BeautyCode.DesignPattern.Head.First.Observer
{
    public interface  IObserver
    {
        voidUpdate();
    }
} 
usingSystem; 
usingSystem.Collections.Generic; 
usingSystem.Linq; usingSystem.Text; namespace BeautyCode.DesignPattern.Head.First.Observer
{
    public interface  ISubject
    {
        IList<IObserver> Observers { get;  set; }
        void Register(IObserver observer);
        void UnRegister(IObserver observer);
        voidNotifyObserver();
    }
} 
usingSystem; 
usingSystem.Collections.Generic; 
usingSystem.Linq; usingSystem.Text; namespace BeautyCode.DesignPattern.Head.First.Observer
{
   public  class SubjectImpl:ISubject 
    {
       public SubjectImpl()
       {
           _observers = new List<IObserver>(0);
       }
       private IList<IObserver> _observers;
        public IList<IObserver> Observers
        {
            get { return _observers; }
             set { this._observers=value ; }
        }

        public void Register(IObserver observer)
        {
            _observers.Add(observer);
        }

        public void UnRegister(IObserver observer)
        {
            _observers.Remove(observer);
        }

        public void NotifyObserver()
        {
            if (_observers != null)
            {
                foreach (IObserver ob in_observers)
                {
                    ob.Update();
                }
            }
        }
    }
} 
usingSystem; 
usingSystem.Collections.Generic; 
usingSystem.Linq; usingSystem.Text; namespace BeautyCode.DesignPattern.Head.First.Observer
{
   public  class ObserverImpl:IObserver 
    {
       private string _name;
       public ObserverImpl(string name)
       {
           _name = name;
       }
        public void Update()
        {
            Console.WriteLine("{0}收到通知了",_name );
        }
    }
} 

客户端调用代码


 Console.WriteLine("====Head.First.Observer=================================================");
            Head.First.Observer.ISubject subject = new Head.First.Observer.SubjectImpl();
            Head.First.Observer.IObserver ob1 = new Head.First.Observer.ObserverImpl("ob1");
            Head.First.Observer.IObserver ob2 = new Head.First.Observer.ObserverImpl("ob2");
            Head.First.Observer.IObserver ob3 = new Head.First.Observer.ObserverImpl("ob3");
            subject.Register(ob1);
            subject.Register(ob2);
            subject.Register(ob3);
            subject.NotifyObserver();

从上面的代码结构中可以看出来如果需要增加一个新的观察者,只需要新观察者实现IObserver接口,然后再主题中register就可以了。

但是上面的观察者模式也还是有一点强依赖的意味,就是主题类依赖于观察者列表,有新的观察者就需要主题类主动注册。又没有可能主题也不关心有没有什么新观察者,只是专注于属于自己的职责-通知主题的内容变化。

其实在.NET中有一种天然存在的设计模式,就是观察者。大家都知道.NET是事件驱动的形式,就是控件的事件引发方法的调用,让触发事件的一方得到信息。我们可以将这个原理引入观察者模式的实现上来。

耗子,猫、狗、主人的故事

usingSystem; 
usingSystem.Collections.Generic; 
usingSystem.Linq; 
usingSystem.Text; namespace ConsoleApp
{ 
    public delegate void ObserverEventHandler(object sender, EventObjectArgs e);
    /// <summary> /// 被观察者
    /// </summary>      public interface IEventObserable
    {

         event ObserverEventHandler ObserverEvent;
         void OnObserverEvent(EventObjectArgs e);
         void RaiseObserverMethod();
    }
    /// <summary> /// 观察者
    /// </summary>      public interface IEventObserver
    {

    }

    public class EventObjectArgs : EventArgs
    {
        private string _message;
        public string Message
        {
            get { return _message; }
        }
        public EventObjectArgs()
        {
        }
        public EventObjectArgs(string message)
        {
            this._message = message;
        }
    }
    class EventCat : IEventObserable
    {
        #region IEventObserable 成员 public event ObserverEventHandler ObserverEvent;

        public  void OnObserverEvent(EventObjectArgs e)
        {
            if (null != ObserverEvent)
            {

                ObserverEvent(this, e);
            }
        }

        public void RaiseObserverMethod()
        {
            Console.WriteLine("猫叫了");
            OnObserverEvent(new EventObjectArgs("猫叫了"));
        }

        #endregion }class EventMouse : IEventObserver
    {
        public EventMouse() { }
        public EventMouse(IEventObserable  observable)
        {
            observable.ObserverEvent += new ObserverEventHandler(observable_ObserverEvent);
        }

       public  void observable_ObserverEvent(object sender, EventObjectArgs e)
        {
            Console.WriteLine(string.Format("{0},所有老鼠被吓跑了", e.Message));
        }
    }
    class EventDog : IEventObserver
    {
        public EventDog(IEventObserable observable)
        {
            observable.ObserverEvent += new ObserverEventHandler(observable_ObserverEvent);
        }

        public void observable_ObserverEvent(object sender, EventObjectArgs e)
        {
            Console.WriteLine(string.Format("{0},所有老鼠被吓跑了,但是狗拿耗子多管闲事", e.Message));
        }
    }
    class EventMaster : IEventObserver
    {
        public EventMaster(IEventObserable observable)
        {
            observable.ObserverEvent += new ObserverEventHandler(observable_ObserverEvent);
        }

       public  void observable_ObserverEvent(object sender, EventObjectArgs e)
        {
            Console.WriteLine(string.Format("{0},所有老鼠被吓跑了,但是狗拿耗子多管闲事,主人生气了,要打死这些动物", e.Message));
        }
    }
}

OO原则:

封装变化

多用组合,少用继承

针对接口编程,不针对实现编程

为交互对象之间的松耦合设计而努力

OO模式

观察者模式-在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。

五、工厂模式

今天给大家带来的是:工厂模式。

我们在代码中创建一个对象,我们会很自然的想到new。其实,除了new意外,我们还有很多的方式可以创建对象。不说复杂的模式,就说简单的语法,其实.NET框架中还有下面的方法。根据需要创建的对象的类型,动态创建对象。

Activator.CreateInstance

通过前面我们已经讲过一条原则:

针对接口编程,不针对实现编程。

使用new创建对象很明显就是一个针对实现编程,new后面需要一个具体的类,不允许你new一个接口或者是抽象类。包括用Activator.CreateInstance都不能创建接口或者抽象类。

那是不是就是说以后不能或者是不要再用new了,都用其他方式吗?

也不是的,也要根据情况来分析,不是一概而论的,在后面会给出一些简单的标准,更多的要结合具体的设计定夺。

很多时候我们会碰到下面的代码:


     string userType = "";
                if (userType == "admin")
                {//创建管理员                  }
                else if (userType == "supervisor")
                {//创建超级管理员                  }
                else if (userType == "coommon")
                {
                    //创建普通用户  } 

如果以后增加了用户的类型,不仅要创建用户类型实体,还需要打开创建用户的这段代码,后面加上一个if。。。else。。。。

如果这段代码没有封装,就更麻烦了,散落在项目的各处,每个地方都要打开,ctrl+c,ctrl+v。开始了无尽的噩梦。

对于后面的维护成了问题,而且违反了一条原则:

OCP原则,对增加开放,对修改封闭。

首先使用封装变化原则对这段代码做一些抽象,上面这段明显是变化较多的地方,其他的代码可能是现实创建的用户的姓名之类的。变成一个方法,然后将用户类型定义为enum类型,减少调用者由于输入错误导致程序错误。


usingSystem; 
usingSystem.Collections.Generic; 
usingSystem.Linq; usingSystem.Text; namespace BeautyCode.DesignPattern.Head.First.Factory
{
    public enum UserType
    {
        Admin,
        Supervisor,
        Common
    }
    public abstract class User
    {
        public virtual UserType UserType
        {
            get;
            protected set;
        }
        public User(UserType userType)
        {
            UserType = userType;
        }
        public virtual string Username
        {
            get;
            set;
        }
        public virtual int Age
        {
            get;
            set;
        }
        public virtual void DisplayName()
        {
            Console.WriteLine("my name is {0}, i'm {1} years old.", Username, Age);
        }
    }
    public class AdminUser : User
    {
        public AdminUser()
            : base(UserType.Admin)
        { }
    }
    public class CommonUser : User
    {
        public CommonUser()
            : base(UserType.Common)
        { }
    }
    public class SupervisorUser : User
    {
        public SupervisorUser()
            : base(UserType.Supervisor)
        {
        }
    }
} 

usingSystem; 
usingSystem.Collections.Generic; 
usingSystem.Linq; usingSystem.Text; namespace BeautyCode.DesignPattern.Head.First.Factory
{
   public  class UserFactory
    {
       public static User CreateSingleUser(UserType userType)
       {
           User user = null;
           switch (userType)
           {
               case UserType.Admin :
                   user = new AdminUser(); 
                   break;
               case UserType.Common :
                   user = new CommonUser();
                   break;
               case  UserType.Supervisor :
                   user = new SupervisorUser();
                   break;
               default :
                   break;
           }
           returnuser;
       }
    }
} 

调用代码

Head.First.Factory.User user =Head.First.Factory.UserFactory.CreateSingleUser(Head.First.Factory.UserType.Admin); 

以后增加新的用户类型就不用在各处修改了,只要新建一个类型实体,然后打开工厂方法,修改一下就可以了。

有没有更好的呢?

能否只是增加一个用户类型实体类呢?

答案是:能

修改一下工厂方法,利用泛型和Activator


 public static T CreateSingleUser2<T>() where T:User
       {
           T user = null;
           user = Activator.CreateInstance<T>();
           return user;
       }

调用者的代码修改为

Head.First.Factory.User user1 = Head.First.Factory.UserFactory.CreateSingleUser2<Head.First.Factory.AdminUser>();

有没有更好的呢?

期待大家的参与讨论!!!!

其实我这个还不算是真正意义上的工厂模式,顶多算是个简单的工厂,后面我会补充一些更好的例子。

感谢大家耐心看完本篇博文。

定义

工厂模式:定义了一个创建对象的借口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到了子类。

上面的描述中提到了“子类决定”的决定,希望不要错误理解,不是模式允许子类本身在运行的时候做决定,而是指在编写创建者类的时候,不需要知道实际创建的类是哪一个。调用者选择使用哪个类,自然就决定了实际创建的对象。

相关文章

网友评论

      本文标题:《深入浅出设计模式》

      本文链接:https://www.haomeiwen.com/subject/bdotfltx.html