1. Factory Pattern
1) concept
Factory Pattern is one of the most commonly used design patterns in Java. This type of design pattern belongs to the creation pattern, which provides the best way to create objects.
In factory mode, we do not expose the creation logic to the client when creating objects, and point to newly created objects by using a common interface.
2) intention
Define an interface for creating objects, let its subclasses decide which factory class to instantiate by themselves, and the factory pattern delays its creation until the subclasses.
3) scenario
It mainly solves the problem of interface selection.
(1) Logger
Records may be recorded on local hard disks, system events, remote servers, etc. Users can choose where to log.
(2) Database access
When the user does not know what kind of database the final system uses, and the database may change.
(4) Designing a framework for connecting servers
We need three protocols, "POP3", "IMAP", "HTTP", which can be used as product classes to implement an interface together.
Note: As a class creation pattern, factory method patterns can be used wherever complex objects need to be generated. One thing to note is that complex objects are suitable for factory mode, while simple objects, especially those that can be created through new, need not use factory mode. If you use factory mode, you need to introduce a factory class, which will increase the complexity of the system.
2. Simple Factory (Static Factory)
1) concept
Define a simple factory using static methods. (Note that it's a factory!)
2) scenario
The factory class is responsible for creating fewer objects.
Objects decide which instance of product class to create, but they don't care how to create objects. For example, if you go to KFC and say you want chicken legs, French fries, drinks or drinks, then KFC is a factory and the client just needs to specify what he wants.
3) Advantages and disadvantages
Excellent:
There is no need to instantiate objects by creating them.
Missing:
(1) Poor scalability
If I want to add a shape, besides adding a specific class of shape, I need to modify the factory class method.
Inheritance cannot change the behavior of creating methods.
It violates the principle of high cohesive responsibility allocation. Because the factory class centralizes the creation logic of all instances, it centralizes all the creation logic into one factory class; the classes it can create can only be considered beforehand, and if new classes need to be added, they need to be changed.
It is not supported when different products need different additional parameters.
4) realization
The factory class is a concrete class (ShapeFactory), a non-interface Abstract class. There is an important create() method (getShape), which dynamically determines which instance of a product class (which inherits from a parent class or interface) should be created based on the parameters passed in. Use if or switch to create the product and return the required instance.
(2) The create() method is usually static, so it is also called static factory.
5)demo
Create a Shape interface whose subclasses implement the factory interface and return an abstract product.
The factory class ShapeFactory uses ShapeFactory to obtain Shape objects. It will pass information to ShapeFactory (CIRCLE / RECTANGLE / SQUARE) to get the type of object it needs.
Create an interface
public interface Shape {
void draw();
}
Creating Entity Classes for Implementing Interfaces
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
Create a factory to generate pairs of entity classes based on given information
public class ShapeFactory {
//Getting Objects of Shape Type Using getShape Method
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
(4) Using the factory, the object of entity class can be obtained by transferring type information.
ShapeFactory shapeFactory = new ShapeFactory();
//Get the Circle object and call its draw method
Shape shape1 = shapeFactory.getShape("CIRCLE");
//Calling Circle's draw method
shape1.draw();
//Get the Rectangle object and call its draw method
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//Call the draw method of Rectangle
shape2.draw();
//Get the Square object and call its draw method
Shape shape3 = shapeFactory.getShape("SQUARE");
//Calling Square's draw method
shape3.draw();
6) Using Reflection to Realize Simple Factory
A simple factory implemented by reflecting Class. forName (clz. getName (). newInstance ():
public class StaticNoodlesFactory {
/**
* Import Class to instantiate noodle product class
*
* @param clz
* @param <T>
* @return
*/
public static <T extends INoodles> T createNoodles(Class<T> clz) {
T result = null;
try {
result = (T) Class.forName(clz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
//Lanzhou hand-pulled noodles
INoodles lz = StaticNoodlesFactory.createNoodles(LzNoodles.class);
lz.desc();
//Instant noodles
INoodles pm = StaticNoodlesFactory.createNoodles(PaoNoodles.class);
pm.desc();
Advantages: When adding a product, you don't need to modify the code of create().
Disadvantage: Because Class. forName (clz. getName (). newInstance () calls a parametric constructor to generate an object, which is the same as new Object(), and the factory method should be used for initialization of complex objects, which is powerless when a parametric constructor needs to be called, like a factory for a factory.
This is not recommended because, like a simple new object, factory methods should be used to initialize complex objects, like factories for factories.
3. Factory method (recommended)
1) concept
An interface for creating objects is defined, but it is up to the subclass to decide which class to instantiate (because the creator class does not need to specify which product to actually create), and the factory method lets the class defer the instantiation to the subclass.
It is a common object-creating design pattern. The core is the invariable part of the encapsulation class. The individualized and changeable part of the encapsulation class is extracted as an independent class, which can be decoupled, reused and extended easily through dependency injection.
2) scenario
There are a lot of different Pizza classes, and only at runtime can you specify which one to optimize. These are the changing parts, and if the menu changes, this part of the code will also change. At this time, the volatile part is extracted as an independent class.
3) Advantages and disadvantages
Excellent:
Decouple the "implementation" of the product from the "use".
Complete realization of the "open-close principle" and scalability
4)demo
Its core structure has four roles: Abstract factory; concrete factory; Abstract product; concrete product.
public class Main {
public static void main(String[] args){
/**
* Factory mode
* Depending on FoctoryType: "one" and "two", let the class defer instantiation to subclasses
*/
Factory factory = new OneFactory();
Product product = factory.FactoryMethod("one");
System.out.println(product.productName);
product = factory.FactoryMethod("two");
System.out.println(product.productName);
}
}
/**
* Factory abstract class
* Create a framework for subclasses to decide how to implement specific products
* @author luo
*
*/
public abstract class Factory {
public Product FactoryMethod(String productType){
Product product = CreateProduct(productType);
return product;
}
/**
* Let subclasses implement what products to produce
*/
public abstract Product CreateProduct(String productType);
}
/**
* Product Abstract classes:
* All products implement this interface, so that classes using these products can refer to this class for a long time.
* @author luo
*
*/
public abstract class Product {
public String productName;
}
/**
* Specific product categories
* @author luo
*
*/
public class OneProduct extends Product{
public OneProduct(){
productName = "oneProduct";
}
}
/**
* Specific product categories
* @author luo
*
*/
public class TwoProduct extends Product{
public TwoProduct(){
productName = "twoProduct";
}
}
/**
* Specific factory categories
* @author luo
*
*/
public class OneFactory extends Factory{
@Override
public Product CreateProduct(String productType) {
switch(productType){
case "one":
return new OneProduct();
case "two":
return new TwoProduct();
default:
break;
}
return null;
}
}
5) Application of Android
(1) java.util.concurrent.Executors class
The java.util.concurrent.Executors class is a factory that generates Executors. It uses a multi-method static factory pattern:
For example, the ThreadPoolExecutor class construction method has five parameters, three of which are fixed, the first two parameters can be configured, as follows:
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
Another example is that JDK wants to add a method to create ForkJoinPool class, and only wants to configure the parallelism parameter, it adds the following method to the class:
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
4. Abstract Factory
1) concept
Provides an interface for creating families of related or dependent objects without specifying specific classes explicitly.
Each method of this interface is responsible for creating a specific product. At the same time, we use subclasses of abstract factories to provide these specific practices, and use factory methods to implement specific factories.
2) scenario
If an object family (or a group of unrelated objects) has the same constraints, the abstract factory pattern can be used
For example:
When an application needs to run on three different platforms, it can shield the influence of the operating system on the application by abstract factory mode, and process the information interacting with the operating system by different product classes.
3) Advantages and disadvantages
Excellent:
It separates specific classes.
(2) It makes it easy to exchange product lines.
(3) It is conducive to product consistency.
Missing:
It is difficult to support new kinds of products.
When factories are abstracted, there may be explosion-like problems.
For example, every time we expand new product categories, not only for food and drink, but also for sleeping and bed service. This requires modifying abstract factory classes. Therefore, all specific factory subclasses are involved and need to be modified synchronously.
4)demo
The factories mentioned above are all single product lines. The abstract factory is a multi-product system.
For example, every store (factory) not only sells noodles, but also drinks.
Beverages are offered for sale. Beverages are products. First, a product category is abstracted. Beverages:
public abstract class IDrinks {
/**
* Describe how much each drink costs
*/
public abstract void prices();
}
Then two specific product categories are realized: Coke and water.
public class ColaDrinks extends IDrinks {
@Override
public void prices() {
System.out.println("Coke is three dollars and five dollars.");
}
}
public class WaterDrinks extends IDrinks {
@Override
public void prices() {
System.out.println("Poor people like me drink water and don't want money.~!");
}
}
The Abstract hotel is nothing more than eating and drinking (abstract factory class):
public abstract class AbstractFoodFactory {
/**
* Noodles production
*
* @return
*/
public abstract INoodles createNoodles();
/**
* Production of beverages
*/
public abstract IDrinks createDrinks();
}
Specific factory categories: Lanzhou Hotel, KFC.
public class LzlmFoodFactory extends AbstractFoodFactory {
@Override
public INoodles createNoodles() {
return new LzNoodles();//Lanzhou Ramen
}
@Override
public IDrinks createDrinks() {
return new WaterDrinks();//Selling water
}
}
public class KFCFoodFactory extends AbstractFoodFactory {
@Override
public INoodles createNoodles() {
return new PaoNoodles();//KFC actually sells instant noodles
}
@Override
public IDrinks createDrinks() {
return new ColaDrinks();//Selling coke
}
}
Use:
AbstractFoodFactory abstractFoodFactory1 = new KFCFoodFactory();
abstractFoodFactory1.createDrinks().prices();
abstractFoodFactory1.createNoodles().desc();
abstractFoodFactory1= new LzlmFoodFactory();
abstractFoodFactory1.createDrinks().prices();
abstractFoodFactory1.createNoodles().desc();
5. Control Inversion (IoC) and Dependency Injection (DI) (not recommended)
The two have similar meanings. It is a comprehensive application of factory mode and reflection mechanism.
1) concept
Inversion of Control (IoC)
Delegating the right to create objects to the framework is an important feature of the framework, not a specific term for object-oriented programming. It includes Dependency Injection (DI) and Dependency Lookup.
IoC can be regarded as a new design pattern, but the theory and time matured relatively late and not included in GoF.
IoC (Inverse of Control) is the core of Spring container. AOP, declarative transaction and other functions blossom and bear fruit on this basis. But the Spring framework is for enterprise-level development. Android uses the java framework to occupy too much memory and is not recommended.
(2) Interface Driver
Interface drivers can provide different flexible subclass implementations to increase code stability and robustness.
2) scenario
New requirements and objects can also be added without recompiling the deployment.
3) Advantages and disadvantages
1.
Controlling inversion puts object generation in XML. When we need to change an implementation subclass, it will become very simple (such objects are usually implemented in some interface), as long as we modify the XML, we can realize the hot plugging of the object.
Deficiency
I > The steps to generate an object become more complex (in fact, it's still quite simple to operate), and it's a bit awkward and intuitive for people who aren't used to it.
II > IoC object generation uses reflection transformation, which reduces efficiency, but improves maintainability and flexibility.
III > refactoring is difficult. If you change the class name, you need to modify it manually in the xml file type.
Lack of IDE refactoring support, if you need to rename a class in Eclipse, then you need to go to the XML file to change it manually, which seems to be the flaw of all XML methods.
4) realization
(1) Dependency Search
Containers provide callback interfaces and context conditions to components. In this way, components must use API s provided by containers to find resources and collaboration objects. The only inversion of control is reflected in those callback methods: the container will invoke these callback methods so that the application code can obtain the relevant resources.
Description from the container perspective: Container controls the application and injects external resources required by the application backwards from the container.
(2) Dependency Injection
Dependency Injection (DI)
DI is an important object-oriented programming principle to reduce the coupling problem M of computer programs, which reduces the coupling between components.
From an application perspective: The application relies on the container to create and inject the external resources it needs.
The application configuration and dependency specification can be separated from the actual application code. Configuration of the relationship between application components through text configuration files, without rewriting and compiling specific code.
DI uses interface programming to dynamically inject objects when they are used.
There are three injection methods:
I > Contructor Injection
The simplest dependency injection method.
public class MovieLister {
private MovieFinder finder;
public MovieLister(MovieFinder finder) {
this.finder = finder;
}
...
}
II > setter injection
public class MovieLister {
s...
public void setFinder(MovieFinder finder) {
this.finder = finder;
}
}
III > interface injection
Interface injection uses interfaces to provide setter methods, which are implemented in the following ways.
The first step is to create an interface for injection use.
public interface InjectFinder {
void injectFinder(MovieFinder finder);
}
class MovieLister implements InjectFinder {
...
public void injectFinder(MovieFinder finder) {
this.finder = finder;
}
...
}