一、开篇乱弹
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>();
有没有更好的呢?
期待大家的参与讨论!!!!
其实我这个还不算是真正意义上的工厂模式,顶多算是个简单的工厂,后面我会补充一些更好的例子。
感谢大家耐心看完本篇博文。
定义
工厂模式:定义了一个创建对象的借口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到了子类。
上面的描述中提到了“子类决定”的决定,希望不要错误理解,不是模式允许子类本身在运行的时候做决定,而是指在编写创建者类的时候,不需要知道实际创建的类是哪一个。调用者选择使用哪个类,自然就决定了实际创建的对象。
网友评论