- 简单工厂模式概述
- 定义:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类
- 在简单工厂模式中用于被创建实例的方法通常为静态(static)方法,因此简单工厂模式又被成为静态工厂方法(Static Factory Method)
- 需要什么,只需要传入一个正确的参数,就可以获取所需要的对象,而无需知道其实现过程
- 例如,我开一家披萨店,当客户需要某种披萨并且我这家店里也能做的时候,我就会为其提供所需要的披萨(当然是要钱的哈哈),如果其所需的我这没有,则是另外的情况,后面会谈。这时候,我这家 披萨店就可以看做工厂(Factory),而生产出来的披萨被成为产品(Product),披萨的名称则被称为参数,工厂可以根据参数的不同返回不同的产品,这就是简单工厂模式
- 简单工厂模式的结构与实现
abstract class Product
{
public void MethName()
{
//公共方法的实现
}
public abstract void MethodDiff();
//声明抽象业务方法
}
class ConcreteProductA : Product
{
public override void MethodDiff()
{
//业务方法的实现
}
}
class Factory
{
public static Product GetProduct(string arg)
{
Product product = null;
if(arg.Equals("A")
{
product = new ConcreteProductA();
//init
}
else if(arg.Equals("B"))
{
product = new ConcreteProductB();
//init
}
else
{
....//其他情况
}
return product;
}
}
class Program
{
static void Main(string[] args)
{
Product product;
product = Factory.GetProduct("A");//工厂类创建对象
Product.MethName();
product.MethodDiff();
}
}
- 简单工厂模式的简化
- 为了简化简单工厂模式,将抽象产品类和工厂类合并,将静态工厂方法移到抽象产品类中
- 客户端可以调用产品父类的静态工厂方法,根据不同的参数创建不同类型的产品子类对象。
- 简单工厂模式的优缺点和适用环境
- 简单工厂模式的优点
(1)工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责 (2)客户端无需知道所创建具体产品的类名,只需知道参数即可 (3)也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。(这也是我在开始的披萨店里遇到没有的披萨的解决情况)
- 简单工厂模式的缺点
(1)工厂类集中了所有产品的创建逻辑,职责过重,一旦异常,整个系统将受影响 (2)使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度 (3)系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂 (4)简单工厂模式使用了static工厂方法,造成工厂角色无法形成基于继承的等级结构。
- 简单工厂模式的适用环境
(1)工厂类负责创建对的对象比较少,因为不会造成工厂方法中的业务逻辑过于复杂
(2)客户端只知道传入工厂类的参数,对如何创建对象不关心
- 简单案例
- 题目: 使用简单工厂模式设计一个可以创建不同几何图形(Shape),如Circle,Rectangle,Triangle等绘图工具类,每个几何图形均具有绘制Draw()和擦除Erase()两个方法 要求在绘制不支持的几何图形时,抛出一个UnsuppShapeException异常,绘制类图并使用C#语言实现。
- UML:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/*使用简单工厂模式设计一个可以创建不同几何图形(Shape),如Circle,Rectangle,Triangle等绘图工具类,每个几何图形均具有绘制Draw()和擦除Erase()两个方法
* 要求在绘制不支持的几何图形时,抛出一个UnsuppShapeException异常,绘制类图并使用C#语言实现。
*/
namespace SimpleShapeFactory
{
public interface InShape//图形接口 抽象产品类
{
void Draw();
void Erase();
}
public class Circle : InShape//圆形类,具体产品类
{
private static int count; //生成图形计数
string radious;
public Circle()//构造
{
Console.WriteLine("Create Circle");
Console.WriteLine("Input the radious of Circle:");
radious = Console.ReadLine();
}
public void Draw()//实现接口方法
{
int Radious = int.Parse(radious);
Console.WriteLine("Display circle " ( count) " information:");
Console.WriteLine("Circle " count " circumference:" 2 * Radious * 3.14159);
Console.WriteLine("Circle " count " area:" 3.14159 * Radious * Radious);
}
public void Erase()//实现接口方法
{
while (true)
{
Console.WriteLine("Erase current shape(y/n)?");
string choose;
choose = Console.ReadLine();
if (choose.Equals("y") || choose.Equals("Y"))
{
Console.WriteLine("Erase Circle " count " successfully!");
count--;
break;
}
else if (choose.Equals("n") || choose.Equals("N"))
{
Console.WriteLine("Circle " count " successfully saved!");
break;
}
else
{
Console.WriteLine("Input error, re-enter!");
}
}
}
}
class Rectangle : InShape//矩形类,具体产品类
{
private static int count = 0;//生成图形计数
string length;
string wideth;
public Rectangle()//构造
{
Console.WriteLine("Create Rectangle");
Console.WriteLine("Input the length and wideth of Rectangle:");
length = Console.ReadLine();
wideth = Console.ReadLine();
}
public void Draw()//实现接口方法
{
int Length = int.Parse(length);
int Wideth = int.Parse(wideth);
Console.WriteLine("Display rectangle " ( count) " information:");
Console.WriteLine("Rectangle " count "circumference:" 2 * Length * Wideth);
Console.WriteLine("Rectangle " count "area:" Length * Wideth);
}
public void Erase()//实现接口方法
{
while (true)
{
Console.WriteLine("Erase current shape(y/n)?");
string choose;
choose = Console.ReadLine();
if (choose.Equals("y") || choose.Equals("Y"))
{
Console.WriteLine("Erase rectangle " count "successfully!");
--count;
break;
}
else if (choose.Equals("n") || choose.Equals("N"))
{
Console.WriteLine("Rectangle " count " successfully saved!");
break;
}
else
{
Console.WriteLine("Input error, re-enter!");
}
}
}
}
class Triangle : InShape//三角形类,具体产品类
{
private static int count = 0;//生成图形计数
string lengtha;
string lengthb;
string lengthc;
public Triangle()//构造
{
Console.WriteLine("Create Triangle");
Console.WriteLine("Input the lengtha ,lengthb and lengthc of Triangle:");
lengtha = Console.ReadLine();
lengthb = Console.ReadLine();
lengthc = Console.ReadLine();
}
public void Draw()//实现接口方法
{
int Lengtha = int.Parse(lengtha);
int Lengthb = int.Parse(lengthb);
int Lengthc = int.Parse(lengthc);
if ((Lengtha Lengthb > Lengthc) && (Lengtha Lengthc > Lengthb) && (Lengthb Lengthc > Lengtha))
{
double S = (Lengtha Lengthb Lengthc) * 0.5;
double area = Math.Sqrt(S * (S - Lengtha) * (S - Lengthb) * (S - Lengthc));
Console.WriteLine("Display triangle " ( count) " information:");
Console.WriteLine("Triangle " count " circumference:" (Lengtha Lengthb Lengthc));
Console.WriteLine("Triangle " count " area:" area);
Erase();
}
else
{
Console.WriteLine("Create triangle failed!");
}
}
public void Erase()//实现接口方法
{
while (true)
{
Console.WriteLine("Erase shape(y/n)?");
string choose;
choose = Console.ReadLine();
if (choose.Equals("y") || choose.Equals("Y"))
{
Console.WriteLine("Erase tirangle " count " successfully!");
--count;
break;
}
else if (choose.Equals("n") || choose.Equals("N"))
{
Console.WriteLine("Triangle " count " successfully saved!");
break;
}
else
{
Console.WriteLine("Input error, re-enter!");
}
}
}
}
class ShapeFactory//图形工厂类,充当工厂类
{
public static InShape Getshape(string type)//静态工厂方法
{
InShape shape;
shape = null;
if (type.Equals("Circle"))
{
shape = new Circle();
Console.WriteLine("Init set Circle");
shape.Draw();
shape.Erase();
}
else if(type.Equals("Rectangle"))
{
shape = new Rectangle();
Console.WriteLine("Init set Rectangle");
shape.Draw();
shape.Erase();
}
else if (type.Equals("Triangle"))
{
shape = new Triangle();
Console.WriteLine("Init set Triangle");
shape.Draw();
}
else//异常 这里我应该声明调用异常处理类的,那样会更好些
{
Console.WriteLine("UnsupportShapeException!");
Console.WriteLine("Emotional reminders :Pay 1 million$ to Alipay:132****6151 can create every shape you want!!! ");
}
return shape;
}
}
class Program//客户端测试类
{
static void Main(string[] args)
{
while (true)
{
InShape shape;
Console.WriteLine("Please input the shape you want to create");
string str = Console.ReadLine();
shape = ShapeFactory.Getshape(str);//通过静态工厂方法创建产品
Console.ReadLine();
}
}
}
}
优点和缺点
3.1 优点
- 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
3.2 缺点
- 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
- 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,同样破坏了“开闭原则”;在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
4 适用环境
在以下情况下可以使用简单工厂模式:
- 工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。
模式应用
- JDK类库中广泛使用了简单工厂模式,如工具类java.text.DateFormat,它用于格式化一个本地日期或者时间。 public final static DateFormat getDateInstance(); public final static DateFormat getDateInstance(int style); public final static DateFormat getDateInstance(int style,Locale locale);
获取不同加密算法的密钥生成器。
代码语言:javascript复制KeyGenerator keyGen=KeyGenerator.getInstance("DESede");
违背开闭原则
对于上面两种简单工厂模式的实现方法,如果我们要添加新的 parser,那势必要改动到 RuleConfigParserFactory 的代码,那这是不是违反开闭原则呢?实际上,如果不是需要频繁地添加新的 parser,只是偶尔修改一下 RuleConfigParserFactory 代码,稍微不符合开闭原则,也是完全可以接受的。
尽管简单工厂模式的代码实现中,有多处 if 分支判断逻辑,违背开闭原则,但权衡扩展性和可读性,这样的代码实现在大多数情况下(比如,不需要频繁地添加 parser,也没有太多的 parser)是没有问题的。
参考文章
更多相关知识和参考文章来源可以关注我的博客站点