Create an extendable factory pattern to allow module users to extend your factory class
To begin with, I’d like to give a quick introduction to the plant’s design pattern. When using different classes of the same kind of “thing” it is useful to abstract instantiate those classes into the factory class/function. While these factories are written by a library/module developer, users of these libraries cannot simply add or override the classes the factory uses. Therefore, we need a simple solution to allow the end developers to extend the Factory class. To explain this pattern in practice,
I will use the truthcR . library
react-chat-module, which can be found here. This type of tutorial is of course convertible to other programming languages, which support reflection (C#, Java, …), see example in C# at the end.
As mentioned, I will explain the idea using the library Chat library reaction. This library allows a developer to implement a simple and flexible conversation within their interaction application using only a single component.
Like many other chat or messaging apps, several types of messages are supported (text, voice, video, photo, …).
To carry out these different types of messages a
MessageFactory createdAnd Which generates the correct message type. For example, such a plant can look like this:
Don’t worry, we’ll clean up this code in a few minutes, but you can see that the react component is returned depending on the file
MessageType It is represented as a union type for the supported message types (
file In this case).
All messages except
text It can also contain a text message (such as naming an image), and then these two types of messages are combined into a child element.
Instead of the static method, you can also implement Factory as a functional component, so you can streamline it like any other component. As you can see, message type is strongly typed using union type but cannot be extended by library users.
To fix this, we have two different sub-problems: how to extend the union type and how to pass additional classes, which must be enforced or overridden by the factory?
To extend types declared in a module, TypeScript has a feature called Module Augmentation. The only problem is that union types cannot be incremented or combined. To get around this problem, we can define an interface from which we create our union type:
The initial value of
MessageType It is the same, as in the previous example, except that we create it using the interface.
With this setting, we can now increase
MessageType An interface within another module (for example in our app, when we want to include the component):
This will add a new type “Test” to our union type and we can add it as a type in ChatMessage user interface.
Now it’s time to start with the hard part: expanding our plant to provide class or add bypass. Initially, I created a type to add as a support, where new or dominant plant classes could be defined:
hasText An element of the interface is closely related to the application, so it may be superfluous for your task.
As you can see, I have defined a record consisting of a string (the new type identifier,
MessageType In this case) and the React component to use in place of or in addition to (this works for another type of class as well and doesn’t just work with React).
We can then add a function type, which will create our instances of factory classes:
Next, we have to add two functions (arrow): the first to create dynamic factory instances and the second to create predefined instances. These functions might look like this:
You can see in the dynamic factory instantiation function, which we check first, if it is available
MessageType Exists in the provided custom factories object, if it isn’t void is returned.
If there is a factory class assigned to a specific type, we instantiate the given component using
React.createElement (This works with ‘normal’ classes as well as with reflection, see here for more info).
In the Create Instances function of the predefined Factory class, the main code remains the same, except that all code paths return an object that consists of the component and whether you want to add a text component or not.
At the end of the function, I added a debug warning that displays when the user added a message with a custom type but did not specify a factory class. We can combine these two functions into the functional component
MessageType like him:
As you can see, we first call the function of the custom factory class, to achieve the basic function.
To add a text message I used a recursive call to the same component, which is added if the component type supports text (which is specified in the return or
CustomFactory object), this might be obsolete for your use case.
The last thing we want to do is get rid of this very ugly streak. To do this, we add an object of type
CustomFactories And set all factory default classes, then we can remove the function
CreateFactoryInstance Instead, call
CreateDynamicFactoryInstance With a built-in object from our default classes and
overrideFactory The object that looks like this: