src.main.java.com.mgnt.lifecycle.management.package-info Maven / Gradle / Ivy
/**
* This package contains some small infrastructure that simplifies and automates working with Factories that provide
* concrete implementations of an Interface. The package contains just 2 classes:
* {@link com.mgnt.lifecycle.management.BaseEntityFactory} and {@link com.mgnt.lifecycle.management.BaseEntity}. In short
* what this infrastructure does is that if you create a factory that extends
* {@link com.mgnt.lifecycle.management.BaseEntityFactory} and some Interface with all its concrete implementations
* extending {@link com.mgnt.lifecycle.management.BaseEntity} then each your concrete implementation class instances will be
* automatically inserted into your factory. You won't have to worry about how and when to populate your factory. The
* infrastructure will do it for you when the constructor of your concrete implementation class is invoked. So all you
* will have to do is to create any number of concrete implementation classes and make sure that for each one constructor
* is invoked. After that you can use your factory to get any of your concrete implementation classes anywhere in your code.
* This is short explanation. There are few more details but not that many.
*
*
* Lets show it with an example. This infrastructure has Package {@code com.mgnt.lifecycle.management.example}
* that has some sub-packages that contains source code example that demonstrate how this infrastructure is used. So in
* this javadoc the same classes will be used. Please see the source code of that package to get all the details.
* Say you have an Interface called
* {@link com.mgnt.lifecycle.management.example.InfoFormatter} that has a single method
* {@link com.mgnt.lifecycle.management.example.InfoFormatter#formatMessage(java.lang.String)}. Obviously this method
* takes some text and formats it according some logic that would be implemented in each concrete implementation.
*
* Next we need to create a factory for our concrete implementations. So we have a factory class
* {@link com.mgnt.lifecycle.management.example.InfoFormatterFactory} that extends
* {@link com.mgnt.lifecycle.management.BaseEntityFactory} This class look like this:
*
* package com.mgnt.lifecycle.management.example;
*
* import com.mgnt.lifecycle.management.BaseEntityFactory;
* import java.util.Collection;
*
* public class InfoFormatterFactory extends BaseEntityFactory<InfoFormatter> {
* private static InfoFormatterFactory FACTORY = new InfoFormatterFactory();
*
* private InfoFormatterFactory() {
* }
*
* public static InfoFormatterFactory getFactoryInstance() {
* return FACTORY;
* }
*
* public static InfoFormatter getInstance(String key) {
* return FACTORY.getEntity(key);
* }
*
* public static Collection<InfoFormatter> getAllInstances() {
* return FACTORY.getAllEntities();
* }
* }
*
*
*
*
* For the purposes of this infrastructure it is recommended that a single abstract parent class extending
* {@link com.mgnt.lifecycle.management.BaseEntity} and implementing your interface is created. Then all your concrete
* implementations will extend this class. So in our case we will have
* {@link com.mgnt.lifecycle.management.example.BaseInfoFormatter} that extends {@link com.mgnt.lifecycle.management.BaseEntity}
* and implements {@link com.mgnt.lifecycle.management.example.InfoFormatter}.
* This class should look like this:
*
*
* public abstract class BaseInfoFormatter extends BaseEntity<BaseInfoFormatter> implements InfoFormatter {
*
* // This is mandatory part of the code for the infrastructure to work
* private static final String FACTORY_TYPE = BaseInfoFormatter.class.getSimpleName();
*
* static {
* init(FACTORY_TYPE, InfoFormatterFactory.getFactoryInstance());
* }
*
* public BaseInfoFormatter() {
* super(FACTORY_TYPE);
* }
*
* public BaseInfoFormatter(String customName) {
* super(FACTORY_TYPE, customName);
* }
*
* // The end of mandatory part
*
* // Some business logic methods that are common to all concrete implementations
* //...
*
* //Implementation of interface declared method
* //...
* }
*
*
* Then we have 2 concrete implementations: {@link com.mgnt.lifecycle.management.example.implementations.JsonInfoFormatter}
* and {@link com.mgnt.lifecycle.management.example.implementations.XmlInfoFormatter} (both of them extending their abstract
* parent class {@link com.mgnt.lifecycle.management.example.BaseInfoFormatter}). Here is how one of them might look,
* (the second looks very similar so it is ommitted here)
*
* public class JsonInfoFormatter extends BaseInfoFormatter {
* private final static String CUSTOM_NAME = "JSON";
*
* public JsonInfoFormatter() {
* super(CUSTOM_NAME);
* }
*
* //Implementation of abstract method or overriding methods goes here
* }
*
*
* So this is our row material so to speak.
* So lets see how this is used. Look at the class
* {@link com.mgnt.lifecycle.management.example.implementations.usage.UsageExample} and in particular its methods
* {@link com.mgnt.lifecycle.management.example.implementations.usage.UsageExample#init()} (that is invoked in the main()
* method) and looks as folllows
*
* private static void init() {
* new JsonInfoFormatter();
* new XmlInfoFormatter();
* }
*
*
* and
* {@link com.mgnt.lifecycle.management.example.implementations.usage.UsageExample#printFormattedGreetings()} that looks
* as follows
*
* private static void printFormattedGreetings() {
* InfoFormatter formatter = InfoFormatterFactory.getInstance("JSON");
* System.out.println("JSON greeting: " + formatter.formatMessage(MESSAGE));
* formatter = InfoFormatterFactory.getInstance("XML");
* System.out.println("XML greeting: " + formatter.formatMessage(MESSAGE));
* List<String> allMessages = new ArrayList<>();
* for(InfoFormatter formattedMessage : InfoFormatterFactory.getAllInstances()) {
* allMessages.add(formattedMessage.formatMessage(MESSAGE));
* }
* System.out.println("All greetings: " + allMessages);
* }
*
*
* Note that we simply use {@link com.mgnt.lifecycle.management.example.InfoFormatterFactory#getInstance(java.lang.String)}
* to get ahold of our concrete implementations. This works because method init was invoked before. Note that in init method
* we simply instantiate our concrete implementations and not saving any references to them. Thanks to the infrastructure,
* during its instantiation each instance inserted itself into its factory and that allows it to be accessed through the
* factory. As for the names ("XML" and "JSON") they are in our case defined in each concrete class and passed to the factory
* through use of its parent constructor
* {@link com.mgnt.lifecycle.management.example.BaseInfoFormatter#BaseInfoFormatter(java.lang.String)} which in turn will
* invoke its parent constructor {@link com.mgnt.lifecycle.management.BaseEntity#BaseEntity(java.lang.String, java.lang.String)}.
* However, {@link com.mgnt.lifecycle.management.BaseEntity} provides another constructor
* {@link com.mgnt.lifecycle.management.BaseEntity#BaseEntity(java.lang.String)} where custom name for entity is not
* required. And the entity would be registered in its factory by its class name.
*
*
* That was about how this infrastructure works. Now where would it be convenient and beneficial to use it? Note that in order
* for infrastructure to work we had to invoke constructor for each concrete class. If only someone or something could
* have done that for us, that would be magical. Well, this could be done for us by Spring framework. Remember that Spring
* instantiate all it's defined beans during its initialization. So within Spring context if we simply declare our concrete
* implementations as Spring beans, Spring would instantiate them for us, thus initializing their factory automatically.
* This could be very convenient. Imagine that you have some bean that has a property of type
* {@link com.mgnt.lifecycle.management.example.InfoFormatter}, but which actual implementation would be needed is determined
* at runtime. So at that moment you can use
* {@link com.mgnt.lifecycle.management.example.InfoFormatterFactory#getInstance(java.lang.String)} to access needed
* implementation. This will allow you not to inject ALL your concrete instantiations into your bean and you won't have to
* use Spring BeanFactory to access a Spring defined bean as that would violate non-intrusiveness of Spring (meaning
* you can write components which have no dependency on Spring). Also, If at some later stage you will need to add
* more concrete implementations, all you will have to do is to add your implementation classes to you code and declare
* them to be Spring beans. The rest will be done by Spring and this infrastructure!
*
*/
package com.mgnt.lifecycle.management;