码迷,mamicode.com
首页 > Windows程序 > 详细

C#设计模式之创建类模式:简单工厂模式

时间:2017-12-04 19:06:16      阅读:306      评论:0      收藏:0      [点我收藏+]

标签:特征   产品   原型   表示   工厂方法   chart   height   品种   直接   

这是记录设计模式种的创建型模式的第一篇,所以,在开头要说一些关于创建型模式的含义和意义。

创建型模式

软件系统在运行时,类将被实例化成对象,并由这些被创建的对象协作完成系统中要求的各项业务功能,创建型模式关注对象的创建过程,是一类最常用的设计模式,在软件开发种的应用非常广泛。创建型模式对类的实例化过程进行了抽象,能够将软件中对象的创建和使用分离,对用户隐藏了类的实例的创建细节。

创建型模式描述如何将对象的创建和使用分离,让用户让用户在使用对象时无须关心对象的创建细节,从而降低系统的耦合度,让设计方案更易于修改和扩展。每一个创建型模式都采用不同的解决方案来回答三个问题:创建什么(what)、由谁创建(who)、何时创建(when)。

在GOF设计模式种,包含5种创建型的模式,通常将一种非GOF设计模式----简单工厂模式作为学习其他工厂模式的基础,这6种创建型模式的名称、定义、学习难度和使用频率如下:

①简单工厂模式:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。学习难度:★★☆☆☆,使用频率:★★★☆☆;

②工厂方法模式:定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。学习难度:★★☆☆☆,使用频率:★★★★★;

③抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们的具体的类。学习难度:★★★★☆,使用频率:★★★★★;

④建造者模式:讲一个复杂对象的构建与它的表示分离,使得同样的构建过哦成可以创建不同的表示。学习难度:★★★★☆,使用频率:★★☆☆☆;

⑤原型模式:使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。学习难度:★★★☆☆,使用频率:★★★☆☆;

⑥单利模式:确保一个类只有一个实例,并提供一个全局的访问点来访问这个唯一的实例。学习难度:★☆☆☆☆,使用频率:★★★★☆;

 

简单工厂模式

简单工厂模式没有包含在GOF的23种设计模式种,但是通常将它作为学习其他创建类模式的开头。

作为最简单的设计模式之一,简单工厂模式的设计思想和实现过程都比较简单,其基本的实现流程如下:

技术分享图片

 

简单工厂的定义如下:定义一个工厂类,他可以根据参数的不同返回不同的实例,被创建的实例通常都具有共同的父类

由于在简单工厂模式种用于创建实例的方法通常时静态方法,所以简单工厂模式又被称为“静态工厂模式”,他是一种类创建型模式。简单工厂模式的要点在于:如果需要什么,只需要传入一个正确的参数,就可以获取所需要的对象,而无须知道其创建细节。

下面来看一个例子:

技术分享图片

农场要建立一个直销工厂来销售各种不同的水果,客户只需要告诉工厂水果的名字,工厂就会给客户快递相应的水果。我们根据前述提到的步骤,来逐步建立这个模型:

一、首先我们建立各种水果类:

 public class Apple
    {
        public Apple()
        {
            Console.WriteLine("apple");
        }
        
    }
    public class Orange
    {
        public Orange()
        {
            Console.WriteLine("Orange");
        }

    }

    public class Bnana
    {
        public Bnana()
        {
            Console.WriteLine("Bnana");
        }
    }

这里就建立了几个水果类。接下来我们吧这些产品抽象出来,建立一个具体类的抽象层,这种方式利于扩展,也符合开闭原则。在第二步种我们实际上对代码进行了重构,将具体的类型都重构成抽象类的子类了。实际上我们的代码在其生命周期中会不断的被重构,这也是软件开发的一个规则。

二:建立抽象层然后将具体的水果类继承到这个抽象层下面

public abstract class Fruit
    {
        
    }

建立了抽象层之后,就要进行简单的重构,将刚才建立的具体类继承到这个抽象类的下面。这样的设计的好处在于我们针对抽象编程,利于扩展,具体时怎样利于扩展的,等我们建立其工厂类就知道了。

三、建立工厂类

 public class FruitFactory
    {
        public static Fruit CreateFruit(string fruitName)
        {
            Fruit fruit = null;
            if (fruitName=="apple")
            {
                fruit= new Apple();
            }
            else if (fruitName=="bnana")
            {
                fruit= new Bnana();
            }
            else if (fruitName == "orange")
            {
                fruit= new Orange();
            }
            return fruit;
        }
    }

有用的东西都在工厂内部的逻辑种,首先解释第一个为什么要新增一个抽象的Fruit类:因为工厂要返回一个抽象类Fruit,而里氏代换原则告诉我们“任何基类出现的地方都可以用一个子类来代替”,这也是针对抽象编程的好处,只用一个方法就变现了所有的返回类型,具体这个返回类型要表现出来什么特征,是多态的范畴了。然后就是客户端的调用:

四:客户端调用

 static void Main(string[] args)
        {
            Fruit fruit = FruitFactory.CreateFruit("apple");
            Console.WriteLine(fruit is Apple);//true
            Console.ReadKey();

        }

 

 简单工厂模式的结构比较简单,其核心是工厂类的设计,其结构如下图:

技术分享图片

由上图可知,简单工厂模式包含一下三个角色:

①工厂类(factory):工厂类是简单工厂模式的核心,包含了创建所有产品实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。在工厂类种提供了静态的工厂方法,返回类型为一个抽象类型,这个抽象类型实现了扩展。

②抽象产品角色:它是工厂类所创建的所有对象的父类,封装了各种产品对象的公共方法,它的引入将提高系统的灵活性,使得在工厂方法种只需要提供一个通用的工厂创建实例方法,因为所有创建的具体产品都是它的子类(里氏代换原则原则)。

③具体产品类:他是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每一个具体的产品角色都继承了抽象产品角色(开闭原则)。需要实现抽象产品种声明的抽象方法。

 

C#简单工厂的应用的实例

某软件公司要基于C#语言开发一套图标库,该图标库可以为应用系统提供多种不同外观的图表,例如柱状图、饼状图等,现在使用简单工厂模式来设计该图标库。

 

技术分享图片

 

 还是根据上面说的简单工厂模式的实现步骤进行操作:

一、构建具体的产品类

 public class PieChart : Chart 
    {
        public PieChart() 
        {
            Console.WriteLine("创建饼状图!");
        }
    
        public void Display() 
        {
            Console.WriteLine("显示饼状图!");
        }
    }
 class LineChart : Chart
    {
        public LineChart() 
        {
            Console.WriteLine("创建折线图!");
        }

        public void Display() 
        {
            Console.WriteLine("显示折线图!");
        }
    }
 class HistogramChart : Chart 
    {
        public HistogramChart() 
        {
            Console.WriteLine("创建柱状图!");
        }
    
        public void Display() 
        {
            Console.WriteLine("显示柱状图!");
        }
    }

建立了三个类,分别对应不同的具体的类型。注意这里直接将他们指定为继承Chart了,接下来看一下Chart类

二、上层抽象的建立:

 interface Chart
    {
        void Display();
    }

和上面水果的例子不同的是这里的Chart是一个接口类型,因为我觉得Chart表现的是一种can do的逻辑。

三、建立工厂

 class ChartFactory
    {
        //静态工厂方法
        public static Chart GetChart(string type) 
        {
            Chart chart = null;
            if (type.Equals("histogram")) 
            {
                chart = new HistogramChart();
                Console.WriteLine("初始化设置柱状图!");
            }
            else if (type.Equals("pie")) 
            {
                chart = new PieChart();
                Console.WriteLine("初始化设置饼状图!");
            }
            else if (type.Equals("line")) 
            {
                chart = new LineChart();
                Console.WriteLine("初始化设置折线图!");            
            }
            return chart;
        }
    }

和上面的逻辑一个意思,没什么好说的了。。

四、通过客户端调用:

 static void Main(string[] args)
        {
            
            Chart chart;

            //读取配置文件
            string chartStr = ConfigurationManager.AppSettings["chartType"];
            chart = ChartFactory.GetChart(chartStr); //通过静态工厂方法创建产品
            chart.Display();

            Console.Read();
        }

在通过客户端调用的时候我们使用了配置文件ConfigurationManager.AppSettings["chartType"];,可以通过文件配置来为工厂方法传入参数,而不是直接修改源码,比如我们想要一个柱状图,但是源码里面的客户端代码已经写死了只会生成一个折线图,那么我们必须修改客户端源码来生成我们想要的东西,这违反了开闭原则。

创建对象和使用对象

在一个面向对象的软件系统种,与一个对象相关的职责通常有3种:对象本身所具有的职责、创建对象的职责和使用对象的职责。对象本身的职责容易理解,就是对象自身所拥有的数据(属性)和行为(方法),主要讨论一下后面两个:创建对象职责和使用对象职责

在C#语言种,通常有一下几种方式来创建一个对象:

技术分享图片

毫无疑问使用new关键字来创建一个对象是最快捷的方法,但是灵活性较差,下面通过一个简单的实例来说明:

class Login
{
    private UserDAO udao;
    
    public Login()
           {
        udao = new OracleUserDAO(); //创建对象
    }
    
    public void Execute() 
           {
        //其他代码
        udao.FindUserById(); //使用对象
        //其他代码
    }
}

Login类中创建了一个ORacleUserDao类并在下面的方法种接着使用了这个类。也就是说,Login类既负责创建这个类也负责使用这个类。创建对象和使用对象的职责耦合在一起,会出现非常严重的问题,如果在Login种希望能够使用UserDao的另一个子类,例如将要将Oracle数据库换成SqlServer的,那么只能修改源码去new一个SqlServerUserDao,那么就违反了开闭原则。

当遇到这种情况时,通常的做法是将类的创建职责从Login类中移除。在Login类之外创建对象,Login类只负责只用对象。通过引入工厂类,让客户类不涉及对象的创建。对象的创建者也不会涉及对象的使用。引入工厂类UserDaoFactory后UML的类图表示如下:

技术分享图片

工厂类的引入将降低代码的维护量。如果是UserDao的某个子类的构造函数发生了改变或者要更改或移除某个子类,例如新增了MySql数据库的操作逻辑,只需要更改UserDaoFactory的逻辑,而不会对Login类产生影响。如果是UserDao的接口发生了改变,那么只需要修改Login的逻辑而不会影响到UserDaoFactory。

所有的工厂模式都强调一点:两个类A和B之间的关系应该仅仅是创建或使用种的一种,而不是两个都包含。将对象的创建和使用分离,使得系统更加符合单一职责原则。有利于对功能的复用和系统的维护。

此外,将对象的创建和使用分离还有一个好处就是防止用来实例化一个类型的数据和代码在多个类中到处都是,而将类的创建的相关只是归集到一个类中,也更加体现了单一职责的软件设计原则。因为有的时候创建实例不仅仅是new一个,还需要传入好多的参数。可能还需要配置环境。如果将这些代码散落到每一个创建对象的客户类中,势必会出现代码重复,难以维护的情况,而实际上客户类根本无须承担这些工作。

但是,并不需要为每一个类都配备一个工厂,如果它足够简单。比如string类型,就没有什么StringFactory类来负责创建string的类的实例,因为它足够简单。

简单工厂模式的优缺点和使用环境

简单工厂的优点只要如下:

①实现了对象的创建和使用职责的分离

②客户端无须知道具体的类名,只需要使用就行,也在一定程度上减轻了复杂对象名称的记忆量。

③通过引入配置文件,可以在不修改客户端代码的情况下更换和增加新的具体类,提高了灵活性。

简单工厂的缺点如下:

①工厂集中了所有具体类的创建工作,职责过重,一旦抛出异常,整个系统会受到影响。

②使用简单工厂模式势必会增加系统种类的个数,也就增加了系统的复杂度和理解难度。

③系统扩展困难,一旦要增加新的具体类,就不得不修改工厂类的逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。

④简单工厂模式由于使用了静态的方法,造成工厂角色无法形成基于继承的等级结构,在C#语言中,类的实例没有办法访问静态方法和静态的变量,无法在客户端代码中针对工厂父类编程,二在运行时使用工厂子类来覆盖父类,因此,工厂类不能得到很好的扩展。

 简单工厂的使用环境:

①工厂类负责创建的对象比较少,由于创建的对象比较少,所以不会造成逻辑过于复杂。

②客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

C#设计模式之创建类模式:简单工厂模式

标签:特征   产品   原型   表示   工厂方法   chart   height   品种   直接   

原文地址:http://www.cnblogs.com/pangjianxin/p/7978289.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!