MyException - 我的异常网
当前位置:我的异常网» 设计模式 » 设计模式(零)简单工厂模式

设计模式(零)简单工厂模式

www.MyException.Cn  网友分享于:2013-04-21  浏览:0次
设计模式(0)简单工厂模式

0 设计模式基础

0.0 设计模式的定义

先来看一下设计模式常见的书面定义:

  • 设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
  • 设计模式是指在软件开发中,经过验证的,用于解决在特定环境下,重复出现的、特定问题的解决方案。

设计模式更多的是一种实际应用中经验的基类和总结,并得到了多数人的认可和验证,经过更规范的整理和分类及命名,成为了一种众所周知的知识体系。

0.1 设计模式的分类

一般情况下说到的设计模式都是指Gof著作中所讲述的23中经典设计模式。

  • 创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。

  • 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

  • 行为型模式:模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式(责任链模式)、访问者模式。

1 面向接口编程

上学的时候经常跟室友一起玩儿War3,我一直比较钟情于暗夜精灵族,暗夜精灵有4个灰常厉害的英雄,每个英雄都会有4个技能。刚开始学习面向对象思想的时候,我已经知道“英雄”可以定义成一个类,假设每个英雄出场时候都要秀一下自己华丽的四个技能,我们在构造函数中传递英雄名称,构造不同的英雄对象。

/// <summary>
/// 英雄类
/// </summary>
public class Hero
{
    private readonly string _name; // 英雄名称

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="name">英雄名称</param>
    public Hero(string name)
    {
        _name = name;
    }

    /// <summary>
    /// 秀出自己的技能
    /// </summary>
    public void ShowSkills()
    {
        switch (_name)
        {
            case "DH":
                Console.WriteLine("我是恶魔猎手,我会法力燃烧、献祭、闪避和变身。");
                break;
            case "WD":
                Console.WriteLine("我是守望者,我会暗影突袭、刀阵、闪烁和复仇天神。");
                break;
            case "KOG":
                Console.WriteLine("我是丛林守护者,我会缠绕根须、荆棘光环、自然之力和宁静。");
                break;
            case "POM":
                Console.WriteLine("我是月亮女祭司,我会召唤猫头鹰,灼热之箭,强击光环和流星雨。");
                break;
            default:
                break;
        }
    }
}

英雄轮流秀出的自己的技能(排名不分先后)

class Program
{
    static void Main(string[] args)
    {
        Hero dh = new Hero("DH");
        dh.ShowSkills();

        Hero wd = new Hero("WD");
        wd.ShowSkills();

        Hero kog = new Hero("KOG");
        kog.ShowSkills();

        Hero pom = new Hero("POM");
        pom.ShowSkills();
    }
}

输出结果

1

上面的代码存在的问题主要有2个:

  • 如果某天我不再玩儿暗夜精灵了,改玩儿别的种族,Hero类中的ShowSkills方法就要完全修改一遍。
  • 秀出自己的技能只是一个最基本的行为,英雄还具有攻击、移动、释放技能等行为,每个英雄攻击力、移动速度、释放技能耗蓝却又都各不相同。

这里就涉及到了面向接口编程的重要原则。

通常接口用来定义实现类的外观,提取实现类共有的行为定义。接口类似于一种契约,根据外部应用的需要,约定了实现类应该实现的功能,而具体内部如何实现,应由具体的实现类控制,同时具体的实现类除了要实现接口规定的行为外,还可以根据需要实现自己独有的行为,也就是说实现类的功能应包含但不仅限于接口定义的功能。

由于外部调用和内部实现被接口隔离开了,外部调用只通过接口调用,也就是说只要接口不变,内部具体实现的变化就不会对外部调用产生任何影响。这样使用接口的好处就很明显了,当我们增加其他英雄的时候只需要增加一个实现英雄接口的具体实现类既可,对原有已实现的不分不会造成任何影响。提现了接口“封装隔离”的思想。

定义英雄接口:

/// <summary>
/// 英雄接口定义
/// </summary>
public interface IHero
{
    /// <summary>
    /// 秀技能
    /// </summary>
    void ShowSkills();
}

不同英雄的具体实现:

/// <summary>
/// 恶魔猎手
/// </summary>
public class DH : IHero
{
    /// <summary>
    /// 秀出自己的技能
    /// </summary>
    public void ShowSkills()
    {
        Console.WriteLine("我是恶魔猎手,我会法力燃烧、献祭、闪避和变身。");
    }
}
/// <summary>
/// 守望者
/// </summary>
public class WD : IHero
{
    /// <summary>
    /// 秀出自己的技能
    /// </summary>
    public void ShowSkills()
    {
        Console.WriteLine("我是守望者,我会暗影突袭、刀阵、闪烁和复仇天神。");
    }
}
/// <summary>
/// 丛林守护者
/// </summary>
public class KOG : IHero
{
    /// <summary>
    /// 秀出自己的技能
    /// </summary>
    public void ShowSkills()
    {
        Console.WriteLine("我是丛林守护者,我会缠绕根须、荆棘光环、自然之力和宁静。");
    }
}
/// <summary>
/// 月亮女祭司
/// </summary>
public class POM : IHero
{
    /// <summary>
    /// 秀出自己的技能
    /// </summary>
    public void ShowSkills()
    {
        Console.WriteLine("我是月亮女祭司,我会召唤猫头鹰,灼热之箭,强击光环和流星雨。");
    }
}

修改外部调用部分,同样每个英雄都正确的秀出了自己的技能。

IHero dh = new DH();
dh.ShowSkills();

IHero wd = new WD();
wd.ShowSkills();

IHero kog = new KOG();
kog.ShowSkills();

IHero pom = new POM();
pom.ShowSkills();

Console.ReadLine();

到这里我们就会发现了新的问题,上面说过接口的作用的就是封装隔离,外部调用只知道接口的存在,不应该依赖于具体的实现类。而接口又是没有办法通过new关键字进行实现话的,如何解决这个矛盾?

2 简单工厂模式

简单工厂模式并不包含在上述23种经典设计模式之中,也有人说简单工厂并不能算得上一个设计模式。不管怎么说,简单工厂却是实实在在的简单易用,还记得当年还在学校的时候,由于没有经过实际项目的历练,在编码经验不足的情况下,去试着了解设计模式的时候,大部分设计模式是无法深入理解的,也就只有看到简单工厂模式的时候会惊呼:“原来还可以这样”抑或“原来我一直用的就是简单工厂”。

简单来说,简单工厂就是通过定义一个工厂类,这个工厂类提供了一个创建具体实例的功能,外部调用只需要告诉工厂需要什么类型的实例,工厂负责创建这个实例,外部调用无需关心其具体实现,从而达到真正的接口隔离的目的。

简单工厂类中创建具体实例的方法一般定义为静态方法,从而可以避免在外部调用的时候再new简单工厂的对象,因此简单工厂模式一般也被成为静态工厂模式。

我们定义一个简单工厂类。

/// <summary>
/// 简单工厂类
/// </summary>
public class Factory
{

    /// <summary>
    /// 创建英雄的静态方法
    /// </summary>
    /// <param name="heroName">英雄名称</param>
    /// <returns></returns>
    public static IHero CreateHero(string heroName)
    {
        switch (heroName)
        {
            case "DH":
                return new DH();
            case "WD":
                return new WD();
            case "KOG":
                return new KOG();
            case "POM":
                return new POM();
            default:
                return null;
        }
    }
}

然后外部调用的时候通过简单工厂方法创建各个英雄的实例。

IHero dh = Factory.CreateHero("DH");
dh.ShowSkills();

IHero wd = Factory.CreateHero("WD");
wd.ShowSkills();

IHero kog = Factory.CreateHero("KOG");
kog.ShowSkills();

IHero pom =  Factory.CreateHero("POM");
pom.ShowSkills();

初识简单工厂模式的时候,最大的疑问就是,这个简单工厂类,无非就是把原来在外部调用时创建具体英雄类实例的代码挪了过去,在简单工厂类里面还是一样需要通过new指定具体的类来进行接口的实例化啊,而且还白白多了一个简单工厂类,意义何在?

其实简单工厂类最大的意义还是起到接口隔离的作用,看到隔离这个词,就肯定有隔离的双方(封装体),我们多增加一个简单工厂类,表面上看是多了一个类,并没有减少任何代码,也没有对代码进行大的更改,只是一个英雄实例化代码的位置移动,简单工厂模式的精髓恰恰就是这个具体类实例化代码位置的移动,我们知道,使用接口的目的就是不让外部调用知道封装体内部的实现,在使用简单工厂类之前,我们创建英雄实例的代码很明显的是位于外部调用部分的,这样其实就是没有隔离,由于简单工厂类位于封装体内部的,工厂类是可以知道具体的实现细节的。使用简单工厂类后,相当于这个封装提对外只公开了一个IHero接口及一个工厂类创建英雄的方法给外部调用,这样隔离就很明确了,只是一段代码位置的移动,从设计上来讲,已经发生了本质的变化。

3 简单工厂的几点建议

  • 工厂方法静化

     简单工厂类中创建实例的方法,应为静态方法。

  • 实例创建配置文件化

     实例创建应尽量通过配置文件及反射机制,动态创建,达到能根据某个值,自动判断并创建对应类的实例的目的,这样就可以将庞大的swith语句块消除,同时,实例化部分的修改,只需要修改配置文件即可。

  • 简单工厂模块化

     一个简单工厂可以定义多个创建实例的静态方法,建议按照不同的功能模块,创建不同的工厂类。因为简单工厂类是一个模块封装提的一部分。

4 简单工厂模式的优点

  • 封装

   能够非常简单快捷的实现模块的组件化,组件通过对外公开接口,实现面向接口编程

  • 解耦

    实现了外部调用和具体实现的解耦,增强了系统的健壮性和易维护性。

源码下载

5楼反骨仔(二五仔)
(*^__^*)
4楼小灰灰9527
增加工厂类部分的解释让我明白了,谢谢。
Re: Fonour
@小灰灰9527,谢谢
3楼数据酷软件
学习了。博主辛苦了,通俗易懂。
Re: Fonour
@数据酷软件,谢谢
2楼11点后要睡觉
如果能讲一些实际用上的应用场景就更好了
1楼abc54288
不错,简单易懂。

文章评论

Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
 程序员的样子
程序员的样子
程序员应该关注的一些事儿
程序员应该关注的一些事儿
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
我的丈夫是个程序员
我的丈夫是个程序员
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
代码女神横空出世
代码女神横空出世
程序员的鄙视链
程序员的鄙视链
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
中美印日四国程序员比较
中美印日四国程序员比较
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
总结2014中国互联网十大段子
总结2014中国互联网十大段子
那些争议最大的编程观点
那些争议最大的编程观点
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
我是如何打败拖延症的
我是如何打败拖延症的
Java程序员必看电影
Java程序员必看电影
旅行,写作,编程
旅行,写作,编程
程序员和编码员之间的区别
程序员和编码员之间的区别
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
鲜为人知的编程真相
鲜为人知的编程真相
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
如何成为一名黑客
如何成为一名黑客
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
10个调试和排错的小建议
10个调试和排错的小建议
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
为什么程序员都是夜猫子
为什么程序员都是夜猫子
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
编程语言是女人
编程语言是女人
每天工作4小时的程序员
每天工作4小时的程序员
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
程序员都该阅读的书
程序员都该阅读的书
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
一个程序员的时间管理
一个程序员的时间管理
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有