2015-12-28 20 views
-1

工厂模式通常为具体类创建基类,然后从该基类继承具体类。对于很多应用程序,我们需要知道这个工厂可以创建的具体类的数量。例如,工厂,创建典型形状的对象(圆的,矩形的等),C#代码下面的例子:如何从工厂模式中获取具体类的数量?

public class ShapeFactory 
{ 
    public IShape GetShape(int shapeIndex) 
    { 
     IShape s = null; 
     const int color = 1; 
     const int thickness = 5; 

     switch (shapeIndex) 
     { 
     case 1: s = new Square(color, thickness); 
      break; 
     case 2: s = new Triangle(thickness); 
      break; 
     case 3: s = new Circle(color); 
      break; 
     } 

     return s; 
    } 
} 

用户可能想要知道有多少种形状的由程序的支持。我知道有两种方法可以做到这一点:

  1. 将数字设置为工厂类中的一个常量,并使其对公众可见 。缺点是每次添加新的图形时,都必须手动增加图形的数量。
  2. 创建一个动态容器(C#中的List),其中包含工厂可以创建的具体对象的所有实例 。优点是 ,即使添加了新的Shape类,它也可以自动计算出它可以创建的形状数量 。缺点是 显而易见,每种形状都必须与 一起创建形状请求!

这样做的最好方法是什么?关于这个特定主题的任何最佳实践?

+0

这种工厂的预期公共接口是什么?你能显示一些代码吗? –

+0

你能举一个例子,你什么时候需要知道工厂可以创建的具体类的数量? – adv12

+0

@YacoubMassad,请参阅编辑。 – james

回答

0

您可以将形状类型存储在数组中,然后使用Activator创建实例。这会照顾索引,计数并简化您的创建功能。

static class ShapeFactory 
{ 
    private static readonly Type[] _shapes = new Type[] { typeof(Square), typeof(Triangle), typeof(Circle) }; 

    public static int FactorySize 
    { 
     get 
     { 
      return _shapes.Length; 
     } 
    } 

    public static IShape GetShape(int shapeIndex, params object[] ctorParams) 
    { 
     return (IShape)Activator.CreateInstance(_shapes[shapeIndex], ctorParams); 
    } 
} 
+0

谢谢你,@Marco。这是一个很好的解决方案,但是如何处理带有各种参数的构造函数呢?例如,Square的ctor采用(int color),Triangle的ctor采用(int color,int thickness)等。 – james

+0

@james see edit。只需添加一个对象[]参数即可。有关信息,请参阅文档:https://msdn.microsoft.com/en-us/library/wcxyzt4d.aspx –

+0

感谢您使用此解决方法。我会接受你的回答。然而,这个解决方案将构造细节暴露给调用者(例如不同形状的构造函数参数),这可能不是一个好设计。如果您将代码与原始代码进行比较,您可以看到原始代码只需要一个参数shapeIndex。其他构造函数的参数可以**作为GetShape函数中的常量。如果您能为此提供建议,我将不胜感激。 – james

2

你可以创建一个为你存储常量的Enum。 这也有助于用户通过IDE的自动完成功能了解“可能性”,并且可以防止用户输入数字“越界”,例如在示例中输入“4”。 (请注意,我一般写java ...所以C#是不是我的专长,但你可以做“东西”与此类似),其每次添加时间编辑类

public class ShapeFactory 
{ 
    enum PossibleShapes {CIRCLE, 
        SQUARE, 
        TRIANGLE, // c# allows you to do this (extra comma) on constructors, not sure about Enums, and helps with reducing 'bad' line changes in git/etc. 
        }; 
    public IShape GetShape(PossibleShapes whichShape) 
    { 
     IShape s = null; 

     switch (shapeCode) 
     { 
     case PossibleShapes.SQUARE : s = new Square(color, thickness); 
      break; 
     case PossibleShapes.TRIANGLE: s = new Triangle(thickness); 
      break; 
     case PossibleShapes.CIRCLE: s = new Circle(color); 
      break; 
     } 

     return s; 
    } 
} 

的“问题”一种新的可能性是没有意义的,因为每次你做这个时你都必须编辑这个类,现在你只需要编辑'PossibleShapes'类。 (请注意,我仍然不认为这是工厂模式的正确用法,因为我不知道“颜色”和“厚度”值来自哪里,但至少这比使用反射)

+0

谢谢,@mawalker。但是这并不告诉调用者**可以创建多少个具体的类,这是我首先提出的问题...... – james

+0

您能否详细说明为什么这比使用反射更好? – james

+0

然后只需创建一个静态的#,因为这就是你需要做的,或者是一个返回#的方法。但是,我没有看到 - 为什么您需要将此信息提供给最终用户。这是更好的原因是因为1)它隐藏了构造函数的详细信息,2)反射代价很高3)这种模式中的代码少于添加所有参数检查逻辑以确保参数有效的代码4)可能更多的是我想不起我的头顶,但安全性和可预测性浮现在脑海。 – mawalker

1

这里是一个Builder Pattern例子,我认为是一个更好的例子封装你的对象创建。(您可以使用工厂方法模式,而不是在构建器中为每个Shape创建不同的命名方法)

此外,它允许用户轻松设置颜色/厚度(仍然可以有默认值,但我并没有将它放入此代码示例)

表示由制造商

public class Shape 
{ 
    public Shape() 
    { 
    } 

    public int Color { get; set; } 

    public int Thickness { get; set; } 
} 

建设者抽象出的产品

public interface IShapeBuilder 
{ 
    // Adding NotNull attribute to prevent null input argument 
    void SetColor([NotNull]string colour); 

    // Adding NotNull attribute to prevent null input argument 
    void SetThickness([NotNull]int count); 

    Shape GetShape(); 
} 

具体实施建设者

public class ShapeBuilder : IShapeBuilder 
{ 
    private Shape _shape; 

    public ShapeBuilder() 
    { 
    } 

    public int GetNumberShapesPossible() 
    { 
     //return some # here 
    } 

    public void GetSquare(){ 
     this._shape = new Square(); 
    } 

    public void GetCircle(){ 
     this._shape = new Circle(); 
    } 

    public void SetColor(string color) 
    { 
     this._shape.Color = color; 
    } 

    public void SetThickness(int thickness) 
    { 
     this._shape.Thickness = thickness; 
    } 

    public Shape Build() 
    { 
     return this._shape; 
    } 
} 

导演

public class ShapeBuildDirector 
{ 
    public Shape Construct() 
    { 
     ShapeBuilder builder = new ShapeBuilder(); 

     builder.GetCircle(); 

     builder.SetColour(2); 
     builder.SetThickness(4); 

     return builder.GetResult(); 
    } 
} 

你将必须从某个地方改变一些代码,当你想新的具体类添加到库中。除非你打算把具体的课程打包成某种.dll,否则就没有办法。必须对建筑者/工厂等进行一些编辑。