2010-11-30 75 views
2

来源于维基百科:什么时候工厂适合?

在面向对象的计算机编程 ,工厂是用于创建其他对象的对象 。它是一个 抽象的构造函数,并且可以使用 来实现各种 分配方案。

任何人都可以请解释何时需要或有利的工厂类?

我目前正在一个项目中,我有一个类,并使用构造函数来初始化一个对象(杜!),但它可以失败,并没有初始化。我有一个Success属性来检查它是否正确创建。这是什么时候应该实施工厂类的一个很好的例子?这样Create()方法可以返回null,我可以摆脱Success属性。我有正确的想法吗?

+0

工厂类或工厂方法? – Bozho 2010-11-30 10:00:55

回答

7

工厂情况的教科书示例是当你有一个接口和几个实现,但你不想公开实现。您可以实现一个工厂(方法或类),该工厂根据您传递的参数生成不同实现的实例;但是,由于它通过接口类型返回它们,所以调用者不会因为实现细节而负担沉重。

真实世界的例子:假设你已经定义了一个流读取器的接口,以及从本地文件,网络资源和标准输入读取的实现。然后编写一个工厂方法,它接受一个参数(URI),并返回一个合适的阅读器。调用者不需要知道实现细节。最好的部分是,当你决定要支持另一种输入方法,比如data:URI时,你只需添加另一个实现并将其添加到工厂 - 你不需要在调用代码中改变任何东西。

+0

......那就是OCP的全部内容:)很好的答案。 – Marcus 2010-11-30 13:47:09

+0

谢谢你,这是有道理的 – Marlon 2010-11-30 18:30:49

3

一个工厂的目的是要确定,在运行时间,从而有可能和经常与不可用的数据时,该程序被编译,其中许多class ES,都从virtual方法的一个特定类派生的,应捕获数据你的程序。

问题是,多态语言功能允许您使用不同类型的数据(共享一个公共基类),调用类型适当的行为。为了从中受益,您需要具有不同派生类型的对象。在Comp Sci课程中学习这些知识时,可能需要硬编码创建每个Derived类型的一部分,并通过指向基类的指针与它们一起玩。在复杂的现实世界问题中,不是通过硬编码创建,而是通常从数据库表,文件和套接字等数据到达程序输入。根据您在每个点上看到的内容,您想要创建一个适当类型的对象来表示它,但您可能需要使用编译时间已知类型(指向基类的指针)记录它。然后,不仅可以执行基类承诺的操作 - 其中一些操作可能涉及动态分派到派生类的实现,但您还可以 - 如有必要 - 确切确定数据的实际类型,并据此调用操作。

例如,假设您阅读以下文件,这说明你是如何收集不同类型的数据的每个:

elephant name Tip-Toes partner Mega 
mule name Dare-You mane_length 132 

你有下面的类层次结构来表示这些:

struct Animal 
{ 
    Animal(const std::string& name) : name_(name) { } 

    virtual void eat_from(Supplies&) = 0; // animals must do in their own way... 
    virtual bool can_jump() { return false; } // some animals might, assume not... 

    std::string name_; 
}; 

struct Elephant : Animal 
{ 
    Elephant(const std::string& name, const std::string& partner) 
     : Animal(name), partner_(partner) 
    { } 

    std::string partner_; 

    virtual void eat_from(Supplies&) { supplies.consume(Tofu, 10 * kg); } 
    void swing_trunk(); // something specific to elephants 
}; 

struct Mule : Animal 
{ 
    Mule(const std::string& name, double kgs) : Animal(name), kilograms_(kgs) { } 
    double kilograms_; 

    virtual void eat_from(Supplies&) { supplies.consume(Grass, 2 * kg); } 
    virtual bool can_jump() { return true; } 
}; 

工厂方法的工作是区分大象与骡子并返回适当类型的新对象(衍生自 - 但不简单 - 动物):

Animal* factory(std::istringstream& input) 
{ 
    std::string what, name; 
    if (input >> what && input >> name) 
    { 
     if (what == "elephant") 
     { 
      std::string partner; 
      if (input >> partner) 
       return new Elephant(name, partner); 
     } 
     else if (what == "mule") 
     { 
      double mane_length; 
      if (input >> mane_length) 
       return new Mule(name, mane_length); 
     } 
    } 
    // can only reach here on unparsable input... 
    throw runtime_error("can't parse input"); 
} 

然后,您可以储存动物* S并对其执行操作:

std::vector<Animal*> animals; 
// we expect 30 animals... 
for (int i = 0; i < 30; ++i) animals.push_back(factory(std::cin)); 

// do things to each animal... 
for (int i = 0; i < 30; ++i) 
{ 
    Animal* p_unknown = animals[i]; 

    std::cout << p_unknown->name() << '\n'; 

    if (Elephant* p = dynamic_cast<Elephant*>(p_unknown)) 
     p->swing_trunk(); 
} 

回到你的问题:

我目前工作的一个项目,我有一个类,并使用构造函数初始化一个对象(Duh!),但它可能会失败并且根本不初始化。我有一个Success属性来检查它是否正确创建。这是什么时候应该实施工厂类的一个很好的例子?这样Create()方法可以返回null,我可以摆脱Success属性。我有正确的想法吗?

不,不是工厂有用的情况,因为只涉及一种类型。只要坚持你所拥有的东西(按照OO的意义),但你可以选择抛出异常,中止程序等,而不是设置被调用者可能会或可能不会检查的标志。

+0

很好的答案,因子方法和工厂类有区别吗? – 2012-01-28 10:02:12

+1

“方法”只是“功能”的另一个名称,明确的功能可以放在一个类中,以便将它们可能需要的任何数据和支持功能组合在一起 - 然后你有一个工厂类,但它并不真正改变事物的方式正在工作 - 只是更好地封装它。当你有一个抽象工厂类时,这意味着工厂函数(和/或支持代码)可以是'虚拟的',并且可以调用任何几个工厂实现,这会变得更有趣 - 并且有意义的不同。例如,选择一个“XML解析”vs“JSON解析”工厂,然后使用。 – 2012-01-31 01:05:31

1

我对工厂模式的看法一直有所不同,但我会无论如何给它。

A工厂用于创建一组相关的类型。相关并不意味着它们必须完全实现相同的接口。相关意思是如果你需要实例化一个类型,然后你需要创建另一个对象,第二个对象的具体(实现)类型取决于第一个对象的具体类型,那么你需要一个Factory。

只创建一种类型的对象(没有“关联性”)的工厂不是工厂,而更像是一个策略。

我上面的定义意味着工厂不会像您想象的那样被使用或需要。

而对于无法初始化的对象,我推荐通过抛出异常而不是依赖状态字段的快速失败方法。

1

我会尝试一个简单的答案:)

Wikipedia

  • 对象的创建排除再利用而无需显著复制代码:

    时,使用工厂模式。

  • 创建对象需要访问不适合包含在组合对象中的信息或资源。
  • 创建对象的生命周期管理需要集中以确保一致的行为。

所以我认为你不需要在你的特定情况下工厂。但我会说有一个不伤害。

如果你想返回一个接口并隐藏实现类,那么工厂通常会使用它。

例如:

public final class LoginFactory { 

private final static Map<SomeEnum, LoginInterface> IMPLEMENTERS = new HashMap<SomeEnum, LoginInterface>(); 

static { 
    IMPLEMENTERS.put(SomeEnum.QUICK, new QuickerLoginImpl()); 
    IMPLEMENTERS.put(SomeEnum.SECURE, new SecureLoginImpl()); 
} 

public static LoginInterface getLoginImpl(SomeEnum type) { // my naming is bad ... 
    return IMPLEMENTERS.get(type); 
} 

}

这样你就可以改变你的SecureLoginImplMoreSecureLoginImpl例如没有你的API的用户甚至没有注意到。

你可能也想看看这个维基页面Abstract Factory pattern

相关问题