设计模式——工厂方法模式

最近在看《软件设计精要与模式(第2版)》,先从第6章《.NET中的工厂方法模式》说起。

 

在这一章中,作者先举了一个例子,就是Car和Engine的关系,并以此说明了为何要引入工厂方法模式,对于这个例子,只是引出问题,然后就草草收场了。接着对.NET中WebRequest对象的创建过程中的工厂模式进行了详细的剖析,最后说明了“惯例优于模式”。

 

现在说说具体的问题引入,比如我有一个Engine的类,派生出SolarEngine和GaslineEngine分别表示太阳能发动机和汽油发动机,这些发动机应用到Car这个类上面。Car这个类不用太关注,关键在与Engine类上面。

 

假设我有几个模块,其中用到了Engine下面的具体的类。比如A模块中有一个创建对象的语句

Engineengine=new SolarEngine();

B模块中也有

Engineengine=new SolarEngine();

或者C、D、E…很多模块都是这么写的。

 

现在突然有要求,把上面的所有SolarEngine类用GaslineEngine类来代替,这样的话,就不得不把所有的模块中的上面这句话替换为

Engine engine= new GaslineEngine();

也就是说,一个需求变了,结果所有的模块的代码都要改写。

 

因此,现在希望的结果就是这些模块都不改动,它们会根据其他模块(比如只需更改一个模块)就会自动地创建出GaslineEngine对象出来;更进一步地,当我有新的Engine出现,比如核发动机(NuclearEngine)吧,这些模块不用任何更改就能自动创建NuclearEngine对象。

 

然后作者说明了用工厂模式可以解决这个问题,但只是一笔带过,直接转到对.NET设计中的工厂模式的剖析去了。

 

实际上,这里提出的问题,跟后面.NET中WebRequest的例子还是不同的,为什么这么说?

在这些A,B,C….模块里,我们最希望看到的就一句话

Engineengine=Engine.Create(); //Create是Engine的静态方法

就能创建需要实际需要的对象(不管你是GaslineEngine,还是SolarEngine,还是以后的NuclearEngine)。也就是说在A,B,C…这些模块里根本看不到诸如gasline,solar这样的字眼。

 

而后面.NET的例子跟这个Engine的例子还是有点不一样的,它不是说在什么模块里面,而是当我需要创建的时候,希望用WebRequest.Create(“http://”)就创建HttpWebRequest对象;用WebRequest.Create(“file://”)就创建FileWebRequest对象。如果要套到上面的例子里面,那应该是Engine.Create(“solar”),Engine.Create(“gasline”)…;也就是说,在这些模块里面还是出现了solar这样的信息性字眼。那如果要改成其它的,岂不是所有模块都要改成Engine.Create(“gasline”)?还是不完美吧。

 

下面通过逐渐完善这个例子来说一下我的理解吧(代码尽量简单点,如果看后面那个.NET例子,有很多代码干扰较大)。分为以下三个部分:

1.实现Engine.Create(“solar”)创建SolarEngine实例

2.通过Engine.Create(),不需要更改已经写好的模块自动创建所需对象

3.“惯例优于模式”

 

先来看代码:

namespace ConsoleApplication1
{
    class Class1
    {

        [STAThread]
        static void Main(string[] args)
        {
            Engine engine = Engine.Create("solar");
            engine.StartEngine();

            Engine engine2 = Engine.Create("gasline");
            engine2.StartEngine();
        }
    }



    public class Engine
    {
        public virtual void StartEngine(){}
        public static Engine Create(string name)
        {
            Engine ret = null;
            //这一部分明显的很业余
            if (name == "solar")
                ret = new SolarEngine();
            else if (name == "gasline")
                ret = new GaslineEngine();
            return ret;
        }


    }

    public class SolarEngine : Engine
    {
        public override void StartEngine()
        {
            Console.WriteLine("Solar Engine Start...");
        }
       
    }

    public class GaslineEngine : Engine
    {
        public override void StartEngine()
        {
            Console.WriteLine("Gasline Engine Start...");
        }
    }
  
}


 

在这个例子中,实现了我们所说的Engine.Create(“solar”);创建SolarEngine对象,Engine.Create("gasline");创建GaslineEngine对象,而且没有用到工厂模式,如果要加入新的NuclearEngine,只需要从Engine派生一个NuclearEngine类,然后在Engine的静态方法Create里面再加一个”nuclear”判断就可以了。

 

上面这个就是简单工厂模式,但是这样做,对于一个追求完美的设计者来说,明显地很业余。因为你既然提供了Engine基类,可能就是开放的,如果别人派生出一个其它的新类,比如WaterEngine,你根本就不知道,而且别人还需要知道Create的内部实现,并且去更改这部分代码,岂不是很业余?

 

所以,这个地方我们放弃使用if…else或者switch这样的判断语句,引入一个ArrayList,保存”solar”,”gasline”,这样的关键词,并且当有人派生出NuclearEngine,WaterEngine这样的发动机时,可以修改这个ArrayList,加入”nuclear”,”engine”这样的关键词。

问题是,然后呢?怎么根据这些关键词创建具体的对象呢?

 

方法一:采用反射技术

namespace ConsoleApplication1
{
    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
	//注册Engine,如果需要添加新的Engine,只需要
	//Engine.RegisterNewEngine(“nuclear”,typeof(NuclearEngine))
            Engine.RegisterNewEngine("gasline", typeof(GaslineEngine));
            Engine.RegisterNewEngine("solar", typeof(SolarEngine));

            Engine engine = Engine.Create("solar");
            engine.StartEngine();

            Engine engine2 = Engine.Create("gasline");
            engine2.StartEngine();
        }
    }

	//建立name与类型之间的隐射关系
    public class EngineMaps
    {
        public string enginename = string.Empty;
        public Type enginetype = null;
        public EngineMaps(string _enginename, Type _enginetype)
        {
            enginename = _enginename;
            enginetype = _enginetype;
        }
    }


    public class Engine
{
	//采用静态ArrayList来代替前面的if..else结构
        public static ArrayList enginelist = new ArrayList();
		//如果需要添加新的Engine,只需要注册即可
        public static void RegisterNewEngine(string _name, Type _type)
        {
            enginelist.Add(new EngineMaps(_name, _type));
        }

        public virtual void StartEngine() { }

        public static Engine Create(string name)
        {
            Engine ret = null;
            Type t = null;
            foreach (EngineMaps map in enginelist)
            {
                if (string.Compare(map.enginename, name) == 0)
                {
                    t = map.enginetype;
                    break;
                }
            }
            if (t != null)
            {
                ret =(Engine)Activator.CreateInstance(t);
            }
            return ret;
        }


    }

    public class SolarEngine : Engine
    {
        public override void StartEngine()
        {
            Console.WriteLine("Solar Engine Start...");
        }

    }

    public class GaslineEngine : Engine
    {
        public override void StartEngine()
        {
            Console.WriteLine("Gasline Engine Start...");
        }
    }
}


方法二:在没有反射技术的时候,采用工厂模式

这个也是在这本书的第一版剖析.NET的例子的时候应用的方法。

 

首先为每一个需要创建的Engine设置工厂类(GaslineEngineFactory,SolarEngineFactory),用于创建具体的实例,并且设置一个共同的接口IEngineFacory,与基类Engine对应。

namespace ConsoleApplication1
{
    class Class1
    {

        [STAThread]
        static void Main(string[] args)
        {
            Engine.RegisterNewEngine("gasline",new GaslineEngineFactory());
            Engine.RegisterNewEngine("solar", new SolarEngineFactory());
            Engine engine = Engine.Create("solar");
            engine.StartEngine();

            Engine engine2 = Engine.Create("gasline");
            engine2.StartEngine();
            
        }
    }


    public class EngineMaps
    {
        //建立name与creator的隐射关系,这个地方用IEngineFactory统一了工厂类,如果不用工厂,这个地方传Engine那就无法创建具体的对象了,工厂模式主要就是在这个地方把具体的对象创建工作抽象为一个IEngineFactory了,掩盖了具体的类的名称。
        
        public string enginename = string.Empty;
        public IEngineFactory creator;
        public EngineMaps(string _enginename, IEngineFactory _enginecreator)
        {
            enginename = _enginename;
            creator = _enginecreator;
        }

        public IEngineFactory Creator
        {
            get
            {
                return this.creator;
            }
            set
            {
                creator = value;
            }
        }
    }



    public class Engine
    {
        public static ArrayList enginelist = new ArrayList();
        
        public static void RegisterNewEngine(string _name, IEngineFactory _creator)
        {
            enginelist.Add(new EngineMaps(_name, _creator));
        }

        public virtual void StartEngine() { }
        
        public static Engine Create(string name)
        {
            Engine ret = null;
            foreach (EngineMaps map in enginelist)
            {
                if (string.Compare(map.enginename, name) == 0)
                {
                    ret = map.Creator.MakeEngine();
                    break;
                }
            }
            return ret;
        }
        
    }

    public class SolarEngine : Engine
    {
        public override void StartEngine()
        {
            Console.WriteLine("Solar Engine Start...");
        }

    }

    public class GaslineEngine : Engine
    {
        public override void StartEngine()
        {
            Console.WriteLine("Gasline Engine Start...");
        }
    }
   
    public interface IEngineFactory
    {
        Engine MakeEngine();
    }
    //太阳能发动机工厂创建太阳能发动机的实例
    public class SolarEngineFactory : IEngineFactory
    {
        internal SolarEngineFactory() { }
        public Engine MakeEngine()
        {
            Console.WriteLine("Create Solar Engine...");
            
            return new SolarEngine();
        }

    }

    public class GaslineEngineFactory : IEngineFactory
    {
        internal GaslineEngineFactory() { }
        public Engine MakeEngine()
        {
            Console.WriteLine("Create Gasline Engine...");
            return new GaslineEngine();
        }
    }
}


 

在采用工厂模式的情况下,如果需要扩展其他的Engine,需要

1)-------派生自Engine

public class NuclearEngine : Engine
    {
        public override void StartEngine()
        {
            Console.WriteLine("Nuclear Engine Start...");
        }
    }

 

2)---------添加一个对应的EngineFactory,并实现IEngineFactory接口

public class NuclearEngineFactory : IEngineFactory
    {
        internal NuclearEngineFactory() { }
        public Engine MakeEngine()
        {
            return new NuclearEngine();
        }
    }

 

3)--------通过Engine.RegisterNewEngine进行注册

Engine.RegisterNewEngine("nuclear", new NuclearEngineFactory());

4)-----具体调用过程

Engine engine3 = Engine.Create("nuclear");
engine3.StartEngine();


好了,如果前面的都看懂了,那么第2部分“通过Engine.Create(),不需要更改已经写好的模块自动创建所需对象”就容易实现多了,主要就是将需要创建的具体的对象的信息保存在一个static变量里面就可以了。

staticstring Defaultenginename=”solar”;

这样A,B,C…这些模块不用更改,只需要更改

Engine.Defaultenginename=”gasline”;

或者static Type Defaultenginetype;

Engine.Defaultenginetype=typeof(GaslineEngine);

或者 static IEngineFactory DefaultFactory;

Engine.DefaultFactory= new GaslineEngineFactory();

然后定义一个不带参数的Create(),里面根据前面的静态设置实现就行了。

 

采用上述方法,就能够实现以后的A,B,C…模块不用做任何改动。如果需要更改具体类,只需要采用静态字段设置(调用的方式,而不是去更改已有模块的内部实现);或者如果需要加入新的Engine,也只是需要定义新类,调用RegisterNewEngine注册就可以了。

 

 

Ok,下面来说第三部分——“惯例优于模式”

这个主要是在使用反射进行创建的时候使用,我们在前面的“方法一”的例子当中,定义了一个EngineMaps来表示名称和类的Type之间的隐射关系,而获得Type可以用一个字符串(类名)来得到。这样只要我们在设计时统一规范,那么就能够省掉这个EngineMaps类。比如说表示一个新的Engine类,其必须在System.EngineCenter这个命名空间下面,且类名为

发动机的动力源+”Engine”,创建的时候,Create的参数名为 发动机的动力源(注意大小写)

 

下面来看具体的代码

using System;
using System.Collections;
using System.Reflection;

namespace ConsoleApplication1
{
    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
//注意大小写,要让参数能转化为
//System.EngineCenter.SolarEngine
           System.EngineCenter.Engine engine = System.EngineCenter.Engine.Create("Solar");
            engine.StartEngine();

            System.EngineCenter.Engine engine2 = System.EngineCenter.Engine.Create("Gasline");
            engine2.StartEngine();
        }
    }
}

//定义一个新的命名空间
namespace System.EngineCenter
{
    public class Engine
    {
        public virtual void StartEngine() { }

        public static Engine Create(string name)
        {
            Engine ret = null;
            Type t = null;

            //注意这个参数
            t = Type.GetType("System.EngineCenter." + name+"Engine");

            if (t != null)
            {
                ret = (Engine)Activator.CreateInstance(t);
            }
            return ret;
        }
 }

    public class SolarEngine : Engine
    {
        public override void StartEngine()
        {
            Console.WriteLine("Solar Engine Start...");
        }

    }

    public class GaslineEngine : Engine
    {
        public override void StartEngine()
        {
            Console.WriteLine("Gasline Engine Start...");
        }
    }
}

既然是字符串决定的,那么就可以把这个字符串定义到配置文件当中,这样代码不需要任何更改和重新编译,只需要更改配置文件,就可以根据需要创建所需要的对象了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值