DesignPattern(二) 建立型模式

建立型模式

       建立型模式就是用来建立对象的模式,抽象了实例化的过程。全部的建立型模式都有两个共同点。第一,它们都将系统使用哪些具体类的信息封装起来;第二,它们隐藏了这些类的实例是如何被建立和组织的。建立型模式包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。编程


 单例模式

单例模式指的是确保某一个类只有一个实例,并提供一个全局访问点。解决的是实体对象个数的问题,而其余的建造者模式都是解决new所带来的耦合关系问题。其实现要点有:设计模式

类只有一个实例。问:如何保证呢?答:经过私有构造函数来保证类外部不能对类进行实例化ide

提供一个全局的访问点。问:如何实现呢?答:建立一个返回该类对象的静态方法函数

       具体的代码就很少说了,这个比较经常使用。工具

 


 简单工厂模式

简单工厂类示例代码
/// <summary>
/// 简单工厂类, 负责 炒菜
/// </summary>
public class FoodSimpleFactory
{
    public static Food CreateFood(string type)
    {
        Food food = null;
        if (type.Equals("土豆肉丝"))
        {
            food= new ShreddedPorkWithPotatoes();
        }
        else if (type.Equals("西红柿炒蛋"))
        {
            food= new TomatoScrambledEggs();
        }
        return food;
    }
}
//调用
Food food1 = FoodSimpleFactory.CreateFood("西红柿炒蛋");
food1.Print();
简单工厂模式示例代码

  简单工厂模式解决了客户端直接依赖于具体对象的问题,客户端能够消除直接建立对象的责任,而仅仅是消费产品。简单工厂模式实现了对责任的分割。简单工厂模式又叫静态方法模式(由于工厂类都定义了一个静态方法),由一个工厂类根据传入的参数决定建立出哪种产品类的实例。ui

缺点:

系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,这样就会形成工厂逻辑过于复杂。this

使用场景:

当工厂类负责建立的对象比较少时能够考虑使用简单工厂模式(),客户若是只知道传入工厂类的参数,对于如何建立对象的逻辑不关心时能够考虑使用简单工厂模式spa

使用举例:

.NET中System.Text.Encoding类就实现了简单工厂模式,该类中的GetEncoding(int codepage)就是工厂方法.net

工厂方法模式

       工厂方法模式指的是定义一个建立对象的工厂接口,由其子类决定要实例化的类,将实际建立工做推迟到子类中。它强调的是”单个对象“的变化。其实现要点有:prototype

建立派生于工厂抽象类,即由具体工厂去建立具体产品,既然要建立产品,天然须要产品抽象类和具体产品类了。

       其具体的UML结构图以下所示:

 

在工厂方法模式中,工厂类与具体产品类具备平行的等级结构,它们之间是一一对应关系。

示例代码
/// <summary>
/// 抽象工厂类
/// </summary>
public abstract class Creator
{
    // 工厂方法
    public abstract Food CreateFoddFactory();
}
/// <summary>
/// 西红柿炒蛋工厂类
/// </summary>
public class TomatoScrambledEggsFactory:Creator
{
    /// <summary>
    /// 负责建立西红柿炒蛋这道菜
    /// </summary>
    /// <returns></returns>
    public override Food CreateFoddFactory()
    {
        return new TomatoScrambledEggs();
    }
}
/// <summary>
/// 土豆肉丝工厂类
/// </summary>
public class ShreddedPorkWithPotatoesFactory:Creator
{
    /// <summary>
    /// 负责建立土豆肉丝这道菜
    /// </summary>
    /// <returns></returns>
    public override Food CreateFoddFactory()
    {
        return new ShreddedPorkWithPotatoes();
    }
}
//调用代码
// 初始化作菜的两个工厂()
Creator shreddedPorkWithPotatoesFactory = new ShreddedPorkWithPotatoesFactory();
Creator tomatoScrambledEggsFactory = new TomatoScrambledEggsFactory();

// 开始作西红柿炒蛋
Food tomatoScrambleEggs = tomatoScrambledEggsFactory.CreateFoddFactory();
tomatoScrambleEggs.Print();

//开始作土豆肉丝

Food shreddedPorkWithPotatoes = shreddedPorkWithPotatoesFactory.CreateFoddFactory();
shreddedPorkWithPotatoes.Print();
工厂方法示例代码

使用举例:

.NET 类库中也有不少实现了工厂方法的类,例如Asp.net中,处理程序对象是具体用来处理请求,当咱们请求一个*.aspx的文件时,此时会映射到System.Web.UI.PageHandlerFactory类上进行处理,而对*.ashx的请求将映射到System.Web.UI.SimpleHandlerFactory类中(这两个类都是继承于IHttpHandlerFactory接口的)。

优势:

工厂方法模式经过面向对象编程中的多态性来将对象的建立延迟到具体工厂中,从而解决了简单工厂模式中存在的问题,也很好地符合了开放封闭原则(即对扩展开发,对修改封闭)。


 抽象工厂模式

       上面的工厂方法模式是为了克服简单工厂模式的缺点而设计出来的,简单工厂模式的工厂类随着产品类的增长须要增长额外的代码),而工厂方法模式每一个具体工厂类只完成单个实例的建立,因此它具备很好的可扩展性。可是在现实生活中,一个工厂只建立单个产品这样的例子不多,由于如今的工厂都多元化了,一个工厂建立一系列的产品,若是咱们要设计这样的系统时,工厂方法模式显然在这里不适用,而后抽象工厂模式却能够很好地解决一系列产品建立的问题,

抽象工厂模式指的是提供一个建立一系列相关或相互依赖对象的接口,使得客户端能够在没必要指定产品的具体类型的状况下,建立多个产品族中的产品对象,强调的是”系列对象“的变化。其实现要点有:提供一系列对象的接口。问:如何去实现呢?答:提供多个产品的抽象接口,建立多个产品族中的多个产品对象。问:如何作到呢?答:每一个具体工厂建立一个产品族中的多个产品对象,多个具体工厂就能够建立多个产品族中的多个对象了。

具体的UML结构图以下所示:

 

下面就以生活中 “绝味” 连锁店的例子来实现一个抽象工厂模式。例如,绝味鸭脖想在江西南昌和上海开分店,可是因为当地人的口味不同,在南昌的全部绝味的东西会作的辣一点,而上海不喜欢吃辣的,因此上海的全部绝味的东西都不会作的像南昌的那样辣,然而这点不一样致使南昌绝味工厂和上海的绝味工厂生成全部绝味的产品都不一样

/// <summary>
/// 抽象工厂类,提供建立两个不一样地方的鸭架和鸭脖的接口
/// </summary>
public abstract class AbstractFactory
{
    // 抽象工厂提供建立一系列产品的接口,这里做为例子,只给出了绝味中鸭脖和鸭架的建立接口
    public abstract YaBo CreateYaBo();
    public abstract YaJia CreateYaJia();
}

/// <summary>
/// 南昌绝味工厂负责制做南昌的鸭脖和鸭架
/// </summary>
public class NanChangFactory : AbstractFactory
{
    // 制做南昌鸭脖
    public override YaBo CreateYaBo()
    {
        return new NanChangYaBo();
    }
    // 制做南昌鸭架
    public override YaJia CreateYaJia()
    {
        return new NanChangYaJia();
    }
}

/// <summary>
/// 上海绝味工厂负责制做上海的鸭脖和鸭架
/// </summary>
public class ShangHaiFactory : AbstractFactory
{
    // 制做上海鸭脖
    public override YaBo CreateYaBo()
    {
        return new ShangHaiYaBo();
    }
    // 制做上海鸭架
    public override YaJia CreateYaJia()
    {
        return new ShangHaiYaJia();
    }
}

/// <summary>
/// 鸭脖抽象类,供每一个地方的鸭脖类继承
/// </summary>
public abstract class YaBo
{
    /// <summary>
    /// 打印方法,用于输出信息
    /// </summary>
    public abstract void Print();
}

/// <summary>
/// 鸭架抽象类,供每一个地方的鸭架类继承
/// </summary>
public abstract class YaJia
{
    /// <summary>
    /// 打印方法,用于输出信息
    /// </summary>
    public abstract void Print();
}

//调用代码
AbstractFactory nanChangFactory = new NanChangFactory();
YaBo nanChangYabo = nanChangFactory.CreateYaBo();
nanChangYabo.Print();
YaJia nanChangYajia= nanChangFactory.CreateYaJia();
nanChangYajia.Print();

// 上海工厂制做上海的鸭脖和鸭架
AbstractFactory shangHaiFactory = new ShangHaiFactory();
shangHaiFactory.CreateYaBo().Print();
shangHaiFactory.CreateYaJia().Print();
抽象工厂示例代码

抽象工厂优缺点

抽象工厂模式将具体产品的建立延迟到具体工厂的子类中,这样将对象的建立封装起来,能够减小客户端与具体产品类之间的依赖,从而使系统耦合度低,这样更有利于后期的维护和扩展,这是抽象工厂模式的优势所在,而后抽象模式同时也存在不足的地方。下面就具体看下抽象工厂的缺点(缺点其实在前面的介绍中以已经涉及了):

抽象工厂模式很难支持新种类产品的变化。这是由于抽象工厂接口中已经肯定了能够被建立的产品集合,若是须要添加新产品,此时就必须去修改抽象工厂的接口,这样就涉及到抽象工厂类的以及全部子类的改变,这样也就违背了“开发——封闭”原则。

使用情景

A一个系统不要求依赖产品类实例如何被建立、组合和表达的表达,这点也是全部工厂模式应用的前提。

B这个系统有多个系列产品,而系统中只消费其中某一系列产品

C系统要求提供一个产品类的库,全部产品以一样的接口出现,客户端不须要依赖具体实现。

使用举例:

抽象工厂模式在实际中的应用也是至关频繁的,然而在咱们.NET类库中也存在应用抽象工厂模式的类,这个类就是System.Data.Common.DbProviderFactory,这个类位于System.Data.dll程序集中,该类扮演抽象工厂模式中抽象工厂的角色

public sealed class SqlClientFactory : DbProviderFactory, IServiceProvider


 建造者模式

  在软件系统中,有时须要建立一个复杂对象,而且这个复杂对象由其各部分子对象经过必定的步骤组合而成。例如一个采购系统中,若是须要采购员去采购一批电脑时,在这个实际需求中,电脑就是一个复杂的对象,它是由CPU、主板、硬盘、显卡、机箱等组装而成的,若是此时让采购员一台一台电脑去组装的话真是要累死采购员了。这里就能够采用建造者模式来解决这个问题,咱们能够把电脑的各个组件的组装过程封装到一个建造者类对象里,建造者只要负责返还给客户端所有组件都建造完毕的产品对象就能够了。然而现实生活中也是如此的,若是公司要采购一批电脑,此时采购员不可能本身去买各个组件并把它们组织起来,此时采购员只须要像电脑城的老板说本身要采购什么样的电脑就能够了,电脑城老板天然会把组装好的电脑送到公司。下面就以这个例子来展开建造者模式的介绍。 

建造者模式指的是将一个产品的内部表示与产品的构造过程分割开来,从而可使一个建造过程生成具体不一样的内部表示的产品对象。强调的是产品的构造过程。其实现要点有:将产品的内部表示与产品的构造过程分割开来。问:如何把它们分割开呢?答:不要把产品的构造过程放在产品类中,而是由建造者类来负责构造过程,产品的内部表示放在产品类中,这样不就分割开了嘛。

具体的UML结构图以下所示:

 

示例代码

/// <summary>
/// 以组装电脑为例子
/// 每台电脑的组成过程都是一致的,可是使用一样的构建过程能够建立不一样的表示(便可以组装成不同的电脑,配置不同)
/// 组装电脑的这个场景就能够应用建造者模式来设计
/// </summary>
namespace 设计模式之建造者模式
{
    /// <summary>
    /// 客户类
    /// </summary>
    class Customer
    {
        static void Main(string[] args)
        {
            // 客户找到电脑城老板说要买电脑,这里要装两台电脑
            // 建立指挥者和构造者
            Director director = new Director();
            Builder b1 = new ConcreteBuilder1();
            Builder b2 = new ConcreteBuilder2();

            // 老板叫员工去组装第一台电脑
            director.Construct(b1);

            // 组装完,组装人员搬来组装好的电脑
            Computer computer1 = b1.GetComputer();
            computer1.Show();

            // 老板叫员工去组装第二台电脑
            director.Construct(b2);
            Computer computer2 = b2.GetComputer();
            computer2.Show();

            Console.Read();
        }
    }

    /// <summary>
    /// 小王和小李难道会自愿地去组装嘛,谁不想休息的,这必须有一我的叫他们去组装才会去的
    /// 这我的固然就是老板了,也就是建造者模式中的指挥者
    /// 指挥建立过程类
    /// </summary>
    public class Director
    {
        // 组装电脑
        public void Construct(Builder builder)
        {
            builder.BuildPartCPU();
            builder.BuildPartMainBoard();
        }
    }

    /// <summary>
    /// 电脑类
    /// </summary>
    public class Computer
    {
        // 电脑组件集合
        private IList<string> parts = new List<string>();

        // 把单个组件添加到电脑组件集合中
        public void Add(string part)
        {
            parts.Add(part);
        }

        public void Show()
        {
            Console.WriteLine("电脑开始在组装.......");
            foreach (string part in parts)
            {
                Console.WriteLine("组件"+part+"已装好");
            }

            Console.WriteLine("电脑组装好了");
        }
    }

    /// <summary>
    /// 抽象建造者,这个场景下为 "组装人" ,这里也能够定义为接口
    /// </summary>
    public abstract class Builder
    {
        // 装CPU
        public abstract void BuildPartCPU();
        // 装主板
        public abstract void BuildPartMainBoard();
        
        // 固然还有装硬盘,电源等组件,这里省略

        // 得到组装好的电脑
        public abstract Computer GetComputer();
    }

    /// <summary>
    /// 具体建立者,具体的某我的为具体建立者,例如:装机小王啊
    /// </summary>
    public class ConcreteBuilder1 : Builder
    {
        Computer computer = new Computer();
        public override void BuildPartCPU()
        {
            computer.Add("CPU1");
        }

        public override void BuildPartMainBoard()
        {
            computer.Add("Main board1");
        }

        public override Computer GetComputer()
        {
            return computer;
        }
    }

    /// <summary>
    /// 具体建立者,具体的某我的为具体建立者,例如:装机小李啊
    /// 又装另外一台电脑了
    /// </summary>
    public class ConcreteBuilder2 : Builder
    {
        Computer computer = new Computer();
        public override void BuildPartCPU()
        {
            computer.Add("CPU2");
        }

        public override void BuildPartMainBoard()
        {
            computer.Add("Main board2");
        }

        public override Computer GetComputer()
        {
            return computer;
        }
    }
}
建造者模式示例代码

总结

在建造者模式中,指挥者是直接与客户端打交道的,指挥者将客户端建立产品的请求划分为对各个部件的建造请求,再将这些请求委派到具体建造者角色,具体建造者角色是完成具体产品的构建工做的,却不为客户所知道。

建造者模式主要用于“分步骤来构建一个复杂的对象”,其中“分步骤”是一个固定的组合过程,而复杂对象的各个部分是常常变化的(也就是说电脑的内部组件是常常变化的,这里指的的变化如硬盘的大小变了,CPU由单核变双核等)。

产品不须要抽象类,因为建造模式的建立出来的最终产品可能差别很大,因此不大可能提炼出一个抽象产品类。

在前面文章中介绍的抽象工厂模式解决了“系列产品”的需求变化,而建造者模式解决的是 “产品部分” 的须要变化。

因为建造者隐藏了具体产品的组装过程,因此要改变一个产品的内部表示,只须要再实现一个具体的建造者就能够了,从而能很好地应对产品组成组件的需求变化。

使用举例:

在.NET 类库中,System.Text.StringBuilder(存在mscorlib.dll程序集中)就是一个建造者模式的实现。不过它的实现属于建造者模式的演化,此时的建造者模式没有指挥者角色和抽象建造者角色,StringBuilder类即扮演着具体建造者的角色,也同时扮演了指挥者和抽象建造者的角色


 原型工厂模式

       原型模式指的是经过给出一个原型对象来指明所要建立的对象类型,而后用复制的方法来建立出更多的同类型对象。其实现要点有:给出一个原型对象。问:如何办到呢?答:很简单嘛,直接给出一个原型类就行了。经过复制的方法来建立同类型对象。问:又是如何实现呢?答:.NET能够直接调用MemberwiseClone方法来实现浅拷贝

具体的UML结构图以下所示:

  原型模式用一个原型对象来指明所要建立的对象类型,而后用复制这个原型对象的方法来建立出更多的同类型对象,它与工厂方法模式的实现很是类似,其中原型模式中的Clone方法就相似工厂方法模式中的工厂方法,只是工厂方法模式的工厂方法是经过new运算符从新建立一个新的对象(至关于原型模式的深拷贝实现),而原型模式是经过调用MemberwiseClone方法来对原来对象进行拷贝,也就是复制

示例代码

///火影忍者中鸣人的影分身和孙悟空的的变都是原型模式
class Client
{
    static void Main(string[] args)
    {
        // 孙悟空 原型
        MonkeyKingPrototype prototypeMonkeyKing = new ConcretePrototype("MonkeyKing");
        // 变一个
        MonkeyKingPrototype cloneMonkeyKing = prototypeMonkeyKing.Clone() as retePrototype;
        Console.WriteLine("Cloned1:\t"+cloneMonkeyKing.Id);
        // 变两个
        MonkeyKingPrototype cloneMonkeyKing2 = prototypeMonkeyKing.Clone() as retePrototype;
        Console.WriteLine("Cloned2:\t" + cloneMonkeyKing2.Id);
        Console.ReadLine();
    }
}

/// <summary>
/// 孙悟空原型
/// </summary>
public  abstract class MonkeyKingPrototype
{
    public string Id { get; set; }
    public MonkeyKingPrototype(string id)
    {
        this.Id = id;
    }

    // 克隆方法,即孙大圣说“变”
    public abstract MonkeyKingPrototype Clone();
}
 
/// <summary>
/// 建立具体原型
/// </summary>
public class ConcretePrototype : MonkeyKingPrototype
{
    public ConcretePrototype(string id)
        : base(id)
    { }

    /// <summary>
    /// 浅拷贝
    /// </summary>
    /// <returns></returns>
    public override MonkeyKingPrototype Clone()
    {
        // 调用MemberwiseClone方法实现的是浅拷贝,另外还有深拷贝
        return (MonkeyKingPrototype)this.MemberwiseClone();
//MemberwiseClone是object类型自有的一个方法
    }
}

 
View Code

原型模式的优缺点

原型模式的优势有:

A原型模式向客户隐藏了建立新实例的复杂性

B原型模式容许动态增长或较少产品类。

C原型模式简化了实例的建立结构,工厂方法模式须要有一个与产品类等级结构相同的等级结构,而原型模式不须要这样。

D产品类不须要事先肯定产品的等级结构,由于原型模式适用于任何的等级结构

原型模式的缺点有:

A每一个类必须配备一个克隆方法

B配备克隆方法须要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不必定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。

使用举例:

在.NET中能够很容易地经过实现ICloneable接口(这个接口就是原型,提供克隆方法,至关于与上面代码中MonkeyKingPrototype抽象类)中Clone()方法来实现原型模式,若是咱们想咱们自定义的类具备克隆的功能,首先定义类继承与ICloneable接口并实现Clone方法。在.NET中实现了原型模式的类以下图所示(图中只截取了部分,能够用Reflector反编译工具进行查看):

相关文章
相关标签/搜索