This is the fifth part of the design pattern series.

“Design Pattern Best Practice # 1 — Use Strategic Mode joyfully”

“Design Pattern Best Practice # 2 — Use Pipes happily”

“Design Patterns Best Practice # 3 — Use Proxy Patterns happily”

“Design Pattern Best Practice # 4 — Use Template Mode happily”

What is the factory method pattern

Factory Method patterns, also known as polymorphic Factory patterns, define an interface to create certain products, but let subclasses decide which products to instantiate, deferring product instantiation to subclasses.

When to use the factory method pattern

Factory pattern matching strategy pattern commonly used together, there are many kinds of products (strategy), when the system and each product has multiple instances, suit to use the factory pattern: each product the corresponding factory provide to create different instances of the product function, so as to avoid the caller and products create a logical coupling, perfectly in line with the law of Demeter (at least know principle).

Have fun using the factory method pattern

background

In common development, we often implement such functions in Spring: collect a class of beans with common characteristics (all implement an interface or are marked with an annotation, etc.), and then put them into a container (usually a Map). When used, we can obtain the corresponding Bean according to the Bean’s identity. Take the FormDataHandlerFactory from my previous post, which uses the form id to get the form submission handler:

@Componentpublic class FormDataHandlerFactory { private static final Map<String, FormDataHandler> FORM_DATA_HANDLER_MAP = new HashMap<>(16); /** * Public FormDataHandler getHandler(String formCode) {public FormDataHandler getHandler(String formCode) { return FORM_DATA_HANDLER_MAP.get(formCode); } @Autowired public void setFormDataHandlers(List<FormDataHandler> handlers) { for (FormDataHandler handler : handlers) { FORM_DATA_HANDLER_MAP.put(handler.getFormCode(), handler); }}}Copy the code

Get the FormItemConverterFactory for the form item converter from the form item type:

@Componentpublic class FormItemConverterFactory { private static final EnumMap<FormItemTypeEnum, FormItemConverter> CONVERTER_MAP = new EnumMap<>(FormItemTypeEnum.class); Public FormItemConverter getConverter(FormItemTypeEnum) public FormItemConverter getConverter(FormItemTypeEnum type) { return CONVERTER_MAP.get(type); } @Autowired public void setConverters(List<FormItemConverter> converters) { for (final FormItemConverter converter : converters) { CONVERTER_MAP.put(converter.getType(), converter); }}}Copy the code

I’ve seen a lot of similar code in the systems I’ve seen, where every time you need this functionality, you define a new XxxFactory, or even write the code to get the corresponding Bean directly in the caller, directly violating the single principle. At this point, we are actually close to using the factory method pattern, and we prefer to call this XxxFactory a simple factory. The problem with using such a simple factory over and over again is that it leads to duplicate code, and thus automatically violates the DRY principle (Don’t Repeat Yourself). Although the repeated code is not much, but for our Programmer, writing repeated code is like spitting in our face — it is tolerable, but unbearable!

So based on the above scenario, LET me share my current “best practices” for implementing the factory method pattern based on Spring (if you have better practices, please feel free to comment on them)

plan

The core of a design pattern is to find the parts that change, and then abstract and encapsulate the changes so that the code can meet the basic principles of object orientation. For the factory method pattern, what changes is the product, the factory, so we can first define the abstract product and the abstract factory.

Abstract product (strategy) :

Public interface Strategy<T> {/** * obtain the id of the policy */ T getId(); }Copy the code

Each product must implement the Strategy interface, which means that each product must have a unique identity. Abstract strategy factory:

public abstract class StrategyFactory<T, S extends Strategy<T>> implements InitializingBean, ApplicationContextAware { private Map<T, S> strategyMap; private ApplicationContext appContext; /* @param id @param id */ public S getStrategy(T id) {return strategyMap.get(id); } /** * getStrategyType();} /** * getStrategyType(); @override public void afterPropertiesSet() { All types of beans S Collection < S > strategies. = appContext getBeansOfType (getStrategyType ()). The values (); strategyMap = Maps.newHashMapWithExpectedSize(strategies.size()); For (final S strategy: strategies) {T id = strategy.getid (); strategyMap.put(id, strategy); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { appContext = applicationContext; }}Copy the code

When the Spring container starts, it scans for beans of the type specified by the factory (Class < S >) and registers them with the factory (joining the strategyMap). Therefore, for the production process of products in the factory, we can just lie down with the help of Spring.

Next, we refactor the above two factories based on our abstract product and abstract Factory:

Get the FormDataHandlerFactory of the form submission processor from the form identifier

@Componentpublic class FormDataHandlerFactory extends StrategyFactory<String, FormDataHandler> {
    @Override    protected Class<FormDataHandler> getStrategyType() {        return FormDataHandler.class;    }}
Copy the code

FormDataHandlerFactory just specifies that its product type is FormDataHandler. Of course, we need to modify the FormDataHandler as well:

public interface FormDataHandler extends Strategy<String> { @Override default String getId() { return getFormCode(); } String getFormCode(); CommonResponse<Object> submit(FormSubmitRequest request); }Copy the code

Gets the FormItemConverterFactory for the form item converter from the form item type

@Componentpublic class FormItemConverterFactory extends StrategyFactory<FormItemTypeEnum, FormItemConverter> {
    @Override    protected Class<FormItemConverter> getStrategyType() {        return FormItemConverter.class;    }}
Copy the code

At this point, FormItemConverterFactory only needs to specify the type of the product, and no more duplicate code is written. Similarly, the FormItemConverter needs to be modified:

public interface FormItemConverter extends Strategy<FormItemTypeEnum> { @Override default FormItemTypeEnum getId() { return getType(); } FormItemTypeEnum getType(); FormItem convert(FormItemConfig config); }Copy the code

If we add a new ListDataFetcherFactory that retrieves the list data from the list identifier, then we first define the interface (product) that retrieves the list data:

public interface ListDataFetcher extends Strategy<String> { CommonResponse<JSONObject> fetchData(ListDataFetchRequest request); }Copy the code

Then implement ListDataFetcherFactory (factory) :

@Componentpublic class ListDataFetcherFactory extends StrategyFactory<String, ListDataFetcher> {
    @Override    protected Class<ListDataFetcher> getStrategyType() {        return ListDataFetcher.class;    }}
Copy the code

With abstract product Strategy and abstract factory StrategyFactory, our code perfectly conforms to the DRY principle.

To optimize the

With the help of reflection

With reflection, we can also make factory code much simpler: if the parent class contains generic parameters, and the child class concretized the generic parameters, then the concretized generic type is available at run time. Based on this feature, we can modify StrategyFactory:

public abstract class StrategyFactory<T, S extends Strategy<T>> implements InitializingBean, ApplicationContextAware { ... /** * retrieve the type of the policy by reflection ** @return the type of the policy */ protected Class<S> getStrategyType() {// getClass retrieve the Class of the current runtime instance, Type superClass = getClass().getGenericSuperClass (); ParameterizedType pt = (ParameterizedType) superclass; Type[] actualTypeArguments = pt.getActualTypeArguments(); Type actualTypeArgument = actualTypeArguments[1]; @SuppressWarnings("unchecked") Class<S> result = (Class<S>) actualTypeArgument; return result; }... }Copy the code

The above three factories are easier to write:

@Componentpublic class FormDataHandlerFactory extends StrategyFactory<String, FormDataHandler> {}
Copy the code
@Componentpublic class FormItemConverterFactory extends StrategyFactory<FormItemTypeEnum, FormItemConverter> {}
Copy the code

` – \

@Componentpublic class ListDataFetcherFactory extends StrategyFactory<String, ListDataFetcher> {}
Copy the code

Composition takes precedence over inheritance

The above solution is to specify the type of the policy (S getStrategyType) by subclass through inheritance and with the help of generic reflection. If there are many factory types, adding a new factory class every time can easily lead to “class explosion”. In the above scenario, the part that changes is the type of policy, and in addition to inheritance, we can address this change through composition. Modify our StrategyFactory:

public class StrategyFactory<T, S extends Strategy<T>> implements InitializingBean, ApplicationContextAware { private final Class<S> strategyType; private Map<T, S> strategyMap; private ApplicationContext appContext; /** * create a policy factory ** @param strategyType Type */ public StrategyFactory(Class<S> strategyType) {this strategyType; } @param id @param id * @return @param id */ public S getStrategy(T id) {return strategyMap.get(id); } @override public void afterPropertiesSet() { All types of beans S Collection < S > strategies. = appContext getBeansOfType (strategyType) values (); strategyMap = Maps.newHashMapWithExpectedSize(strategies.size()); For (final S strategy: strategies) {T id = strategy.getid (); strategyMap.put(id, strategy); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { appContext = applicationContext; }}Copy the code

At this time, StrategyFactory is no longer an abstract class, and a new property strategyType is introduced for StrategyFactory, and the construction of StrategyFactory must set the policy (product) type in the current factory. For FormDataHandlerFactory, FormItemConverterFactory, and ListDataFetcherFactory, we don’t need to generate them by inherit, we can combine them directly by configuration:

@Configurationpublic class FactoryConfig {
    @Bean    public StrategyFactory<String, FormDataHandler> formDataHandlerFactory() {        return new StrategyFactory<>(FormDataHandler.class);    }
    @Bean    public StrategyFactory<FormItemTypeEnum, FormItemConverter> formItemConverterFactory() {        return new StrategyFactory<>(FormItemConverter.class);    }
    @Bean    public StrategyFactory<String, ListDataFetcher> listDataFetcherFactory() {        return new StrategyFactory<>(ListDataFetcher.class);    }}
Copy the code

Global marketing team

In the core area of Alibaba e-commerce, it is responsible for connecting the two ends of supply and demand and supporting various products, platforms and solutions in the field of e-commerce marketing, including juhuasuan, tens of billions of subsidies, Tmall U First, Tmall Little Black Box, Tmall new product incubation, brand number and other heavyweight businesses. We are deeply involved in annual promotions such as Double 11, 618, and 99, constantly challenging the limits of technology! We are committed to creating a strong sense of happiness of the technical team, there are deep cultivation of electricity business technology of the old driver, also vigorous xiao Meng new, more can yan can sweet sister, looking forward to have curiosity and thinking of your joining!