This is the 7th day of my participation in the August Text Challenge.More challenges in August

What is the factory model

The factory pattern defines an interface for creating objects and lets subclasses decide which class to create. In other words, the subclass decides what class to create.

In human terms: classes are created by factories. What factories? Look at the business, what business is what factory. That is: defer the creation of a class to a concrete subclass.

We can write the top-level code according to the diagram (see UML class diagram for details if you can’t understand the class diagram):

Abstract Class Creator {// Create a Product. The parameter is the class name. } abstract class Product {// Get Product information abstract String getInfo(); }Copy the code

Next we write the underlying implementation:

Class ConcreteCreator extends Creator {@override Product createProduct(String name) {Product Product = null; try { product = (Product) Class.forName(name).newInstance(); } catch (Exception e) { e.printStackTrace(); } return product; Class ConcreteProduct extends Product {@override String getInfo() {return toString(); }}Copy the code

Ok, let’s look at the use of the business layer:

Public void test() {// Create a factory. Creator = new ConcreteCreator(); // Create a Product with factory Product Product = creator. CreateProduct (" com.test.concretecreator "); System.out.println(product.getInfo()); }Copy the code

According to the above code, we know that the top layer takes Creator to create objects, but Creator is not done at all. Instead, it is implemented by the concrete factory class ConcreteCreator. In other words, the action of creating classes is deferred to the subclass, which corresponds to the above definition.

But, look at this code, do not find where the benefit, it seems to write a lot of extra code ah, we can not write this:

public void test() {
    Product product = new ConcreteProduct();
    System.out.println(product.getInfo());
}
Copy the code

Absolutely, no problem at all. However, if there are multiple places to create the product, for example:

class A { public void test(String type) { Product product = new ConcreteProduct(); System.out.println(product.getInfo()); } } class B { public void test(String type) { Product product = new ConcreteProduct(); System.out.println(product.getInfo()); } } class C { public void test(String type) { Product product = new ConcreteProduct(); System.out.println(product.getInfo()); }} / /... Other classesCopy the code

Then, one day, the creation method of the product needs to be changed and a name must be passed in, so all the places where the product was created need to be changed… As many references to this class as you have… What if we introduced the factory, because the factory is the only one that created this object, so we just need to change the factory, and we don’t need to change A, B, or C.

The idea is simple, in plain English: changing a 1-to-many relationship (one product, multiple locations) to a 1-to-1 relationship (one product, one factory) will only cause one change,

To put it more bluntly, A, B and C are responsible for using the product, and their duty is to use the product, which should not involve the logic of creation. Therefore, A factory should provide this product to me. I don’t need to know the details of the creation of the product, I just know that it can be used. So, one characteristic of the factory pattern is that it hides creation details. Because the creation details of the product are shielded, the high-level logic (the classes that use it) is unaffected if any changes are made to the product, as long as they are not changes in usage.

The factory pattern can degenerate, and when there is only one factory, there is no need to create a factory, just use static methods, such as singleton is simple factory pattern.

Evolution for multiple factories

Through the above, we see that ConcreteCreator the factory class is through the reflection to create objects, there are a lot of shortcomings, low efficiency of 1 2 difficult to use. So can we use reflection? Yes, we can change it as follows:

class ConcreteCreator extends Creator { @Override Product createProduct(String name) { Product product = null; if (name.equals("com.test.ConcreteProduct")) { product = new ConcreteProduct(); } else if (name.equals("com.test.ConcreteProduct2")) { product = new ConcreteProduct2(); } return product; }}Copy the code

As you can see, we add if-else branches by judging the class name to create different products. The problem is that when there are more if-else branches, it becomes unreadable and difficult to maintain. Instead, we can use the multi-factory model, where we have a factory create only one product:

Abstract class Creator {// The class name is not needed. } class ConcreteCreator extends Creator {@override Product createProduct(String name) {return new ConcreteProduct(); } // Factory 2 class ConcreteCreator2 extends Creator {@override Product createProduct(String name) ConcreteProduct2(); }}Copy the code

To use:

Public void test(String type) {// Create factory 1 Creator Creator = new ConcreteCreator(); Product Product = creator. CreateProduct (); // Create factory 2 Creator Creator2 = new ConcreteCreator2(); Product product2 = creator2.createProduct(); }Copy the code

It can be seen that after changing to multiple factories, our logic is much clearer:

  • 1 for the top layer, I used to need to know the name of the product to create the product, but now I just need to find the corresponding factory, and the corresponding factory will create the corresponding product.
  • 2 For the bottom layer, the original new product needs to add if-else to add branches, all in a class, so only one person can change. Now we only need to add one factory to add products. More people can make changes. One person is responsible for one factory.

Our business logic is much clearer, so it will be much easier to maintain extensions later. But! Not quite right, I need a product, you let me build a factory first, why would I build a factory…

Can you do that? I only need to know the contact information of the factory, and then I can contact the factory to produce products for me, which is the most realistic, can! Let’s modify the factory mode.

Factory mode optimization

To remove the action of creating a factory from the underlying logic, we can create a factory creating a factory as follows:

Public static Creator createFactory(class <? extends Creator> claz) { try { return (Creator) Class.forName(claz.getName()).newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; }}Copy the code

We create a factory creator, and then the top-level logic just needs to find it. As follows:

Public void test (String type) {/ / according to factory's contact information for factory 1 1 Creator Creator. = FactoryCreator createFactory (ConcreteCreator. Class) Product Product = creator. CreateProduct (); / / according to the factory's contact information for factory 2 Creator creator2 = FactoryCreator. CreateFactory (ConcreteCreator2. Class) / / factory to create the Product 2 Product product2 = creator2.createProduct(); }Copy the code

As you can see, instead of creating a factory, the top-level logic passes in the factory’s contact information (the.class class), retrieves the corresponding factory, and then retrieves the product.

Of course, creating objects by reflection has its own performance overhead, so we can add a map to cache the factories that we have already created, which can be fetched directly from the map next time.

Private Map<String, Creator> Map = new HashMap<>(); public Creator createFactory(Class<Creator> claz) { try { String name = claz.getName(); // If (map.containskey (name)) return map.get(name); Creator = (Creator) class.forName (claz.getName()).newinstance (); // Add to cache map.put(name, creator); // Return creator; } catch (Exception e) { e.printStackTrace(); } return null; }}Copy the code

For more detailed code, see the final example here

Use of factory patterns

Let’s look at the most common examples in Android source code:

View contentView = LayoutInflater.from(mContext).inflate(R.layout.xxxxxxx, null);
Copy the code

We need to get the LayoutInflater via LayoutInflater. From (mContext).

Public static LayoutInflater from(Context Context) {// We use context.getSystemService () to obtain LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (LayoutInflater == null) { throw new AssertionError("LayoutInflater not found."); } return LayoutInflater; }Copy the code

We know that the final implementation of the Context is ContextImpl, so let’s follow through:

// This name is passed above: Context.LAYOUT_INFLATER_SERVICE @Override public Object getSystemService(String name) { return SystemServiceRegistry.getSystemService(this, name); }Copy the code

Then follow it to SystemServiceRegistry

Public static Object getSystemService(ContextImpl CTX, String name) {ServiceFetcher ServiceFetcher<? > fetcher = SYSTEM_SERVICE_FETCHERS.get(name); // Then fetch the Service via fetch's getService. return fetcher ! = null ? fetcher.getService(ctx) : null; Static abstract Interface ServiceFetcher<T> {T getService(ContextImpl CTX); } private static final HashMap<String, ServiceFetcher<? >> SYSTEM_SERVICE_FETCHERS = new HashMap<String, ServiceFetcher<? > > ();Copy the code

So since there must be a put after get, let’s look for the put:

Private static <T> void registerService(String serviceName, Class<T> serviceClass, ServiceFetcher<T> serviceFetcher) { SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName); System_service_fetchers. put(serviceName, serviceFetcher); }Copy the code

So when is registerService called? We continue with:

Static {// value is a factory for creating LayoutInflaters. CachedServiceFetcher is a factory for creating LayoutInflaters. registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class, New CachedServiceFetcher<LayoutInflater>() {// Then rewrite CachedServiceFetcher createSerivice(). @Override public LayoutInflater createService(ContextImpl CTX) {// Create PhoneLayoutInflater, a subclass of LayoutInflater. PhoneLayoutInflater is not cached here. return new PhoneLayoutInflater(ctx.getOuterContext()); }}); }Copy the code

This is in the static code block of CachedServiceFetcher, and of course there are other initializations of services, such as WindowManagerService, LocationManagerService, etc.

But! We found no code to cache LayoutInflaters! So does every fetch create a new one? Let’s go back to where we got the LayoutInflater:

Fetcher. GetService (); CachedServiceFetcher (); return fetcher ! = null ? fetcher.getService(ctx) : null;Copy the code

Let’s see:

static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> { private final int mCacheIndex; CachedServiceFetcher() { // Note this class must be instantiated only by the static initializer of the // outer class (SystemServiceRegistry), which already does the synchronization, // so bare access to sServiceCacheSize is okay here. mCacheIndex = sServiceCacheSize++; } @override @suppressWarnings ("unchecked") public final T getService(ContextImpl CTX) {// Cache final Object[] cache = ctx.mservicecache; final int[] gates = ctx.mServiceInitializationStateArray; for (;;) { boolean doInitialize = false; Synchronized (cache) {// synchronized (cache) {service = (T) cache[mCacheIndex]; if (service ! = = = null | | gates [mCacheIndex] ContextImpl. STATE_NOT_FOUND) {/ / found, direct return to return the service; } if (gates[mCacheIndex] == ContextImpl.STATE_READY) { gates[mCacheIndex] = ContextImpl.STATE_UNINITIALIZED; } if (gates[mCacheIndex] == ContextImpl.STATE_UNINITIALIZED) { doInitialize = true; gates[mCacheIndex] = ContextImpl.STATE_INITIALIZING; } } if (doInitialize) { T service = null; @ServiceInitializationState int newState = ContextImpl.STATE_NOT_FOUND; // createService() is called, which is the overridden service = createService(CTX); newState = ContextImpl.STATE_READY; } catch (ServiceNotFoundException e) { onServiceNotFound(e); } finally {// synchronized (cache) {// mCacheIndex = service; gates[mCacheIndex] = newState; cache.notifyAll(); } } return service; } synchronized (cache) { while (gates[mCacheIndex] < ContextImpl.STATE_READY) { try { cache.wait(); } catch (InterruptedException e) { Log.w(TAG, "getService() interrupted"); Thread.currentThread().interrupt(); return null; }}}}} // Provides abstract methods to throw exceptions directly. Public Abstract createService(ContextImpl CTX) throws ServiceNotFoundException; }Copy the code

Let’s just look at the comments. The logic is simple: cache through an array with a synchronization lock.

Ok, now let’s get the logic straight:

  • 1 We use LayoutInflater. From (context) to get instances of layoutinflaters.
  • 2 Use context.getSystemService(context.layout_inflATER_service) to internally implement this function.
  • 3 So we find the actual implementation of Context in ContextImpl
  • 4. It is found that through SystemServiceRegistry getSystemService (), so we find SystemServiceRegistry.
  • 5 finds that it gets a factory from a map and then gets it from the factory’s getService().
  • 6 turns out to be put in its static code block.
  • 7 And we found that the real implementation of this factory is CachedServiceFetcher, which has its own cache. When getService() is used, it first looks in the cache, returns when it finds it, and then calls createService() to create it, and then puts it in the cache.

conclusion

We see the beauty of the factory model, where the top layer doesn’t need to care about the details of the object, just delegates to the factory to create it, greatly reducing coupling, and the multi-factory model also improves flexibility and scalability. Through the source code, we see the power of the factory mode, the creation of the action and the cache action can be separated, greatly enhance the flexibility of the creation of object timing.