238 lines
21 KiB
Plaintext
238 lines
21 KiB
Plaintext
/*********************************************************************
|
|
MQL5 Script
|
|
Copyright 2019, dmipec
|
|
792112@gmail.com
|
|
|
|
Factory Method (Virtual Constructor) Programming Pattern
|
|
|
|
This pattern was published in the "Design Patterns: Elements of
|
|
Reusable Object-Oriented Software", 1st Edition, by Erich Gamma
|
|
(Author), Richard Helm (Author), Ralph Johnson (Author), John
|
|
Vlissides (Author), Grady Booch (Foreword). These authors are
|
|
collectively known as Gang of Four (GoF).
|
|
|
|
Structure (diagram)
|
|
|
|
Intent
|
|
Define an interface for creating an object, but let subclasses
|
|
decide which class to instantiate. Factory Method lets a class defer
|
|
instantiation to subclasses.
|
|
|
|
Applicability
|
|
- a class can't anticipate the class of objects it must create.
|
|
- a class wants its subclasses to specify the objects it creates.
|
|
- classes delegate responsibility to one of several helper
|
|
subclasses, and you want to localize the knowledge of which helper
|
|
subclass is the delegate.
|
|
|
|
Participants
|
|
Product
|
|
- defines the interface of objects the factory method creates.
|
|
Concrete Product
|
|
- implements the Product interface.
|
|
Creator
|
|
- declares the factory method, which returns an object of type
|
|
Product.
|
|
Creator may also define a default implementation of the factory
|
|
method that returns a default ConcreteProduct object.
|
|
- may call the factory method to create a Product object.
|
|
Concrete Creator
|
|
- overrides the factory method to return an instance of a
|
|
ConcreteProduct.
|
|
|
|
Collaborations
|
|
Creator relies on its subclasses to define the factory method so
|
|
that it returns an instance of the appropriate ConcreteProduct.
|
|
|
|
Consequences
|
|
Factory methods eliminate the need to bind application-specific
|
|
classes into your code. The code only deals with the Product
|
|
interface; therefore it can work with any user-defined
|
|
ConcreteProduct classes.
|
|
A potential disadvantage of factory methods is that clients might
|
|
have to subclass the Creator class just to create a particular
|
|
ConcreteProduct object. Subclassing is fine when the client has to
|
|
subclass the Creator class anyway, but otherwise the client now must
|
|
deal with another point of evolution.
|
|
Here are two additional consequences of the Factory Method pattern:
|
|
1. Provides hooks for subclasses.
|
|
Creating objects inside a class with a factory method is always
|
|
more flexible than creating an object directly. Factory Method
|
|
gives subclasses a hook for providing an extended version of an
|
|
object.
|
|
A class could define a factory method that creates a default
|
|
object. A subclass can define an application-specific class by
|
|
overriding this factory method. In this case the factory method is
|
|
not abstract but provides a reasonable default implementation.
|
|
2. Connects parallel class hierarchies.
|
|
In the examples we've considered so far, the factory method is
|
|
only called by Creators. But this doesn't have to be the case;
|
|
clients can find factory methods useful, especially in the case
|
|
of parallel class hierarchies.
|
|
Parallel class hierarchies result when a class delegates some of
|
|
its responsibilities to a separate class. Consider objects that
|
|
can be manipulated interactively. Implementing such interactions
|
|
isn't always easy. It often requires storing and updating
|
|
information that records the state of the manipulation at a
|
|
given time. This state is needed only during manipulation;
|
|
therefore it needn't be kept in the object. Moreover, different
|
|
objects behave differently when the user manipulates them.
|
|
With these constraints, it's better to use a separate Manipulator
|
|
object that implements the interaction and keeps track of any
|
|
manipulation-specific state that's needed. Different objects will
|
|
use different Manipulator subclasses to handle particular
|
|
interactions. The resulting Manipulator class hierarchy
|
|
parallels (at least partially) the class hierarchy.
|
|
The class provides a CreateManipulator factory method that lets
|
|
clients create a objects's corresponding Manipulator. Object
|
|
subclasses override this method to return an instance of the
|
|
Manipulator subclass that's right for them. Alternatively, the
|
|
class may implement CreateManipulator to return a default
|
|
Manipulator instance, and object subclasses may simply inherit that
|
|
default. The classes that do so need no corresponding Manipulator
|
|
subclass—hence the hierarchies are only partially parallel.
|
|
Notice how the factory method defines the connection between the
|
|
two class hierarchies. It localizes knowledge of which classes
|
|
belong together.
|
|
|
|
Implementation
|
|
Consider the following issues when applying the Factory Method
|
|
pattern:
|
|
1. Two major varieties.
|
|
The two main variations of the Factory Method pattern are
|
|
(1) the case when the Creator class is an abstract class and
|
|
does not provide an implementation for the factory method it
|
|
declares, and (2) the case when the Creator is a concrete class
|
|
and provides a default implementation for the factory method.
|
|
It's also possible to have an abstract class that defines a
|
|
default implementation, but this is less common.
|
|
The first case requires subclasses to define an implementation,
|
|
because there's no reasonable default. It gets around the
|
|
dilemma of having to instantiate unforeseeable classes. In the
|
|
second case, the concrete Creator uses the factory method
|
|
primarily for flexibility. It's following a rule that says,
|
|
"Create objects in a separate operation so that subclasses can
|
|
override the way they're created." This rule ensures that
|
|
designers of subclasses can change the class of objects
|
|
their parent class instantiates if necessary.
|
|
2. Parameterized factory methods.
|
|
Another variation on the pattern lets the factory method create
|
|
multiple kinds of products. The factory method takes a parameter
|
|
that identifies the kind of object to create. All objects the
|
|
factory method creates will share the Product interface.
|
|
|
|
Code output
|
|
Received Product #2097152 from Creator 1 factory method
|
|
Running Creator 1 operation with Product #3145728
|
|
Received Default product #5242880 from Creator 2 factory method
|
|
Running Creator 2 operation with Default product #6291456 */
|
|
|
|
/*********************************************************************
|
|
Product */
|
|
interface IProduct {
|
|
string Id();
|
|
};
|
|
/*********************************************************************
|
|
Concrete Product 1 */
|
|
class CProduct1: public IProduct {
|
|
public:
|
|
string Id();
|
|
};
|
|
/*********************************************************************
|
|
Concrete Product 2 */
|
|
class CProduct2: public IProduct {
|
|
public:
|
|
string Id();
|
|
};
|
|
/*********************************************************************
|
|
Creator (abstract)
|
|
Non-Parameterized factory method with default implementation */
|
|
class ACreator {
|
|
public:
|
|
virtual string Id() = 0;
|
|
virtual IProduct *FactoryMethod();
|
|
void Operation();
|
|
};
|
|
/*********************************************************************
|
|
Concrete Creator 1 */
|
|
class CCreator1: public ACreator {
|
|
public:
|
|
string Id();
|
|
IProduct *FactoryMethod();
|
|
};
|
|
/*********************************************************************
|
|
Concrete Creator 2 */
|
|
class CCreator2: public ACreator {
|
|
public:
|
|
string Id();
|
|
};
|
|
|
|
/*********************************************************************
|
|
This Client function does not belong to the pattern.
|
|
For demonstration purpose. */
|
|
void Client(ACreator*creator)
|
|
{
|
|
IProduct *product = creator.FactoryMethod();
|
|
printf("Received %s #%d from %s factory method", product.Id(), product, creator.Id());
|
|
creator.Operation();
|
|
delete product;
|
|
delete creator;
|
|
}
|
|
/*********************************************************************
|
|
Script */
|
|
void OnStart()
|
|
{
|
|
//--- creator with factory method override
|
|
Client(new CCreator1);
|
|
//--- creator without override (default factory method)
|
|
Client(new CCreator2);
|
|
}
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
Default Product ID */
|
|
string CProduct1::Id(void)
|
|
{
|
|
return "Default product";
|
|
}
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
Product ID */
|
|
string CProduct2::Id(void)
|
|
{
|
|
return "Product";
|
|
}
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
Default Factory Method */
|
|
IProduct *ACreator::FactoryMethod()
|
|
{
|
|
return new CProduct1;
|
|
}
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
An Operation */
|
|
void ACreator::Operation()
|
|
{
|
|
IProduct *product = FactoryMethod();
|
|
printf("Running %s operation with %s #%d", Id(), product.Id(), product);
|
|
delete product;
|
|
}
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
Creator 1 Factory Method override */
|
|
IProduct *CCreator1::FactoryMethod()
|
|
{
|
|
return new CProduct2;
|
|
}
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
Creator 1 ID */
|
|
string CCreator1::Id(void)
|
|
{
|
|
return "Creator 1";
|
|
}
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
Creeator 2 ID */
|
|
string CCreator2::Id(void)
|
|
{
|
|
return "Creator 2";
|
|
}
|
|
/*********************************************************************
|
|
The code was formatted with Stroustrup C++ style (MetaEditor >
|
|
Tools > Options > Styler). Navigate with Alt-G/M/Arrows */
|