什么时候建工厂合适?

发布于 2024-10-04 23:29:26 字数 314 浏览 0 评论 0原文

摘自维基百科:

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

谁能解释一下工厂类何时需要或有益?

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

Taken from wikipedia:

In object-oriented computer
programming, a factory is an object
for creating other objects. It is an
abstraction of a constructor, and can
be used to implement various
allocation schemes.

Could anyone please explain when a factory class is required or beneficial?

I am currently working on a project where I have a class and use the constructor to initialize an object (duh!) but it can fail and not initialize at all. I have a Success property to check if it was created properly. Is this a good example for when a factory class should be implemented? This way the Create() method can return null and I can get rid of the Success property. Do I have the right idea?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

九命猫 2024-10-11 23:29:26

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

真实世界的示例:假设您已经为流读取器定义了一个接口,以及从本地文件、网络资源和标准输入读取的实现。然后,您编写一个工厂方法,该方法采用单个参数(URI),并返回合适的读取器。调用者不需要了解实现细节。最好的部分是,当您决定要支持另一种输入方法(例如 data: URI)时,您只需添加另一个实现并将其添加到工厂中 - 您无需更改调用代码中的任何内容。

The textbook example for a Factory situation is when you have an interface and several implementations, but you don't want to expose the implementations. You implement a factory (method or class) that produces instances of different implementations based on the parameters you pass it; however, since it returns them by the interface type, the caller will not be burdened with implementation details.

Real-world example: Suppose you have defined an interface for a stream reader, and implementations to read from a local file, a network resource, and stdin. You then write a factory method that takes a single parameter (the URI), and returns a suitable reader. The caller doesn't need to know about implementation details. The best part is, when you decide you want to support another input method, say, data: URIs, you just add another implementation and add it to the factory - you won't need to change anything in the calling code.

梨涡少年 2024-10-11 23:29:26

工厂的目的是在运行时确定,因此在编译程序时可能且经常在数据不可用的情况下,确定许多 中的哪些全部派生自具有 virtual< 的特定类。 /code> 方法,应该为您的程序捕获该数据。

关键是多态语言功能允许您处理不同类型的数据(共享公共基类),调用适合类型的行为。为了从中受益,您需要拥有不同派生类型的对象。在计算机科学课程中学习这一点时,您可能会硬编码创建每种派生类型中的一些,并通过指向基类的指针来使用它们。在复杂的现实问题中,通常不是硬编码创建,而是从程序输入到达的数据驱动,例如数据库表、文件和套接字。根据您在每个点所看到的具体内容,您希望创建一个适当类型的对象来表示它,但您可能需要使用编译时已知类型(指向基类的指针)来保留它的记录。然后,您不仅可以执行基类承诺的操作(其中一些可能涉及动态分派到派生类的实现),而且您还可以(如果需要)准确确定数据的真实类型并相应地调用操作。

例如,假设您阅读以下文件,其中显示了如何为每个数据收集不同类型的数据:

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):

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");
}

然后您可以存储 Animal* 并对其执行操作:

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();
}

回到您的问题:

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

不,在这种情况下工厂没有用处,因为仍然只涉及一种类型。只要坚持你所拥有的(在面向对象的意义上),但你可以选择抛出异常、中止程序等,而不是设置一个被调用者可能会或可能不会费心检查的标志。

A factory's purpose is to determine, at run-time and hence potentially and often with data unavailable when the program was compiled, which of many classes, all derived from a specific class with virtual methods, should capture that data for your program.

The point is that polymorphic language features allow you to work with different types of data (sharing a common base class), invoking the type-appropriate behaviours. To benefit from this, you need to have objects of different derived types. When learning about this in a Comp Sci course, you probably hard-code creation of a few of each of the Derived types and play with them through pointers to the base class. In complex real-world problems, rather than hard-coding creation it is often driven off data arriving from the programs input, such as database tables, files, and sockets. Depending on what exactly you see at each point, you want to create an appropriately-typed object to represent it, but you'll probably need to keep a record of it using a compile-time known type: the pointer to base class. Then, not only can you preform the operations that the base class promises - some of which may involve dynamic dispatch to the derived class's implementation, but you can also - if necessary - determine exactly what the real type of the data is and invoke actions accordingly.

For example, say you read the following file, which shows how you've collected different types of data for each:

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

You have the following class heirarchy to represent these:

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; }
};

The factory method's job is to distinguish elephant from mule and return a new object of the appropriate type (derived from - but not simply - Animal):

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");
}

You can then store Animal*s and perform operations on them:

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();
}

Returning to your question:

I am currently working on a project where I have a class and use the constructor to initialize an object (duh!) but it can fail and not initialize at all. I have a Success property to check if it was created properly. Is this a good example for when a factory class should be implemented? This way the Create() method can return null and I can get rid of the Success property. Do I have the right idea?

No, not a situation where a factory is useful, as there's still only one type involved. Just stick with what you've got (in an OO sense), but you could choose to throw an exception, abort the program etc. rather than set a flag that the called may or may not bother to check.

紫南 2024-10-11 23:29:26

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

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

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

我的上述定义意味着 Factory 并不像您想象的那样被使用或需要。

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

My opinion on the Factory pattern has always been somewhat different, but I'll give it anyway.

A Factory is for creating a set of related types. Related does not mean that they necessarily implement the same interface(s) exactly. Related means that if you have one type you need to instantiate, and then you need to create another object and the concrete (implementation) type of the second object depends on the concrete type of the first object, you need a Factory.

A Factory that only creates one type of object (there is no "relatedness") is not a Factory but more like a Strategy.

My above definitions imply that Factory isn't used or needed as much as you would think.

And for objects that fail to initialize, I recommend the fail-fast approach by throwing an exception instead of relying on a status field.

柳絮泡泡 2024-10-11 23:29:26

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

来自维基百科

在以下情况下使用工厂模式:

  • 对象的创建阻止了重用,而无需大量重复代码。
  • 对象的创建需要访问不适合包含在组合对象中的信息或资源。
  • 创建对象的生命周期管理需要集中化,以确保行为一致。

所以我认为在你的特殊情况下你不需要“需要”工厂。
但我想说拥有一个并没有什么坏处。

工厂的一个常见用途是如果您想返回一个接口并隐藏实现类。

例如: 通过

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);
}

这种

方式,您可以将 SecureLoginImpl 更改为 MoreSecureLoginImpl,而 API 用户甚至不会注意到。

您可能还想查看此 wiki 页面抽象工厂模式

I will try a simple answer :)

From Wikipedia.

Use the factory pattern when:

  • The creation of the object precludes reuse without significantly duplicating code.
  • The creation of the object requires access to information or resources not appropriate to contain within the composing object.
  • The lifetime management of created objects needs to be centralised to ensure consistent behavior.

So I think you don't 'need' a factory in your particular case.
But I'd say having one does not hurt.

A common use for factories would be if you want to return an interface and hide the implementing class.

For instance:

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);
}

}

This way you can change your SecureLoginImpl to MoreSecureLoginImpl for example without the user of your API even noticing.

You might also want to have a look at this wiki page Abstract Factory pattern.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文