Preface:
For learning design patterns, I recommend: HeadFirst Design Patterns and Big Talk Design Patterns. In addition, design patterns promote learning in practice. Before seeing any knowledge, first think about what I can learn, with the problem to look at the problem, will make learning twice the result with half the effort, otherwise it is twice the effort with half the effort.
Don’t get too hung up on the classes and forms of design patterns, just remember that the process of separating change from immutable is a design pattern
Why are design patterns learned and forgotten?
- Not trying (and certainly not being overconfident and wanting to use design mode when you see code)
- Focusing too much on the structure of the design pattern, forgetting the business itself.
- Most of the time, we are stuck in the form and design, tangled in what design mode to use, in fact, the design mode itself is the combination of inheritance, encapsulation, polymorphism, most of the time, as long as the problem can be solved, do not need to use too much skill
- Delirious before study
- It was basically learning to walk in Handan. When I saw others learning design patterns, I went to learn design patterns myself
Many people learn design patterns and then, after a while, find that they don’t use them and forget all about them (me too). So I hope these design patterns are more combined with some more practical needs (try), after all, design patterns are used to learn, if not better not to learn, go to watch anime TV series or something to relax.
What is the Simple Factory pattern?
Realistic understanding:
Simple factories are literally like our factories. If we want to produce something over and over again, we need to build factories to produce it. We need to give the factory instructions, for example, to produce a batch of apples and a batch of bananas. We only need to know the factory production, and don’t need to pay attention to the internal details.
Factory mode:
Simple factory mode is a kind of creation design mode. It defines a simple factory and is responsible for generating the required operation classes for specific operation objects. It separates the creation of objects from the use of objects.
Features of a simple factory:
- Returns an abstract interface or superclass, and the factory manages the subclass creation process
- Make the creation process a black box
- Close the creation process and the client only needs to focus on the result.
Advantages and disadvantages of factory mode:
Advantages:
- Using the method of creating a factory, we decouple the acquisition of concrete objects from the production objects. The production object factory produces the corresponding objects with the parameters we pass in, and the caller only needs to pass the objects that need to be produced to achieve the concrete effect.
- Decouples the creation and creation processes.
- Generate different concrete objects according to different logical judgments.
Disadvantages:
- Each time a factory object concrete implementation class is added, it needs to be added
if/else
Adverse to maintenance - A large number of subclasses can lead to rapid expansion and bloat of the factory class
- The simple factory approach generally deals with simple business logic and is not recommended if the logic is complex to create.
Actual cases:
The following case is a personal understanding, there may be deviations, different people understand the difference. Suggestions are welcome.
Scene simulation:
Our classic nintendo games tanks war, for example, when entering the game level, there will be our tanks and enemy tanks, our tanks and local tanks is not only different shapes, and very brittle, but the enemy tanks according to color need several guns will destroy, so if you use the code to simulate what kind of?
Not using design patterns:
According to the scenario, I designed the chart below
In the normal way, we define a tank’s parent class, and then we need to define three subclasses that inherit from the parent class to implement our own extension. When we need to build a tank, we need to worry about all the details, such as whether to build our tank or the enemy tank, our tank location, the enemy tank location, our health, enemy health, etc., from the creation of the tank to the destruction of the tank, we are involved in all the process.
Java + mytank. Java + Bigtank. Java + test class main.javaCopy the code
The specific code is as follows:
- Abstract classes for tanks:
/** * An abstract class for tanks that defines the behavior of tanks **@author zxd
* @version 1.0
* @date2021/1/25 0:14 * /
public abstract class Tank {
/** * Tank HP */
protected int hp;
/** * tank bullets */
protected List<Object> bullet;
/** * move the method s */
abstract void move(a);
/** * attack */
abstract void attack(a);
/** * stop */
abstract void stop(a);
}
Copy the code
- The mouse tanks
/** * Mouse tank **@author zxd
* @version 1.0
* @date2021/1/25 22:02 * /
public class MouseTank extends Tank implements Runnable {
public void display(a) {
System.err.println("Pointy, like a rat.");
}
public MouseTank(a) {
// Tank assumes only one life
hp = 1;
new Thread(this).start();
bullet = new ArrayList<>();
// Initialize to add six rounds
bullet.add(new Object());
bullet.add(new Object());
bullet.add(new Object());
bullet.add(new Object());
bullet.add(new Object());
}
@Override
void move(a) {
System.err.println("Mouse tank move");
}
@Override
void attack(a) {
System.err.println("Rat tank fire.");
/ /.. Popup bullet
if (bullet.size() <= 0) {
System.err.println("Rat tanks are out of ammo.");
return;
}
// The Mouse tank fires two shots at once
bullet.remove(bullet.get(bullet.size() - 1));
}
@Override
void stop(a) {
System.err.println("Stop");
}
@Override
public void run(a) {
while (true) {
// Start moving once created
move();
// Shoot aimlessly
attack();
attack();
// Pause for one second after completing a round
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Random stop
if (new Random(100).nextInt() % 2= =0) { stop(); }}}}Copy the code
- Giant tanks
/** * Giant tank **@author zxd
* @version 1.0
* @date 2021/1/25 22:14
*/
public class BigTank extends Tank implements Runnable{
public void display(a) {
System.err.println("Giant Tank");
}
public BigTank(a) {
// Colored tanks have many lives
hp = 5;
new Thread(this).start();
bullet = new ArrayList<>();
// Initialize to add three rounds
bullet.add(new Object());
bullet.add(new Object());
bullet.add(new Object());
}
@Override
void move(a) {
System.err.println("Giant tank Move.");
}
@Override
void attack(a) {
System.err.println("Giant tank fire.");
/ /.. Popup bullet
if (bullet.size() <= 0) {
System.err.println("The giant tanks are out of bullets.");
return;
}
// The Mouse tank fires two shots at once
bullet.remove(bullet.get(bullet.size() - 1));
}
@Override
void stop(a) {
System.err.println("Stop the giant tanks.");
}
@Override
public void run(a) {
while (true) {
// Start moving once created
move();
// Shoot aimlessly
attack();
// Pause for 2 seconds after completing a round,
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Stops randomly, not as frequently as mouse tanks
if (new Random(1000).nextInt() % 2= =0) { stop(); }}}}Copy the code
- Our tanks
/** * Our tanks **@author zxd
* @version 1.0
* @date2021/1/25 21:58 * /
public class MyTank extends Tank{
public MyTank(a) {
// Our tank assumes only one life
hp = 1;
bullet = new ArrayList<>();
// Initialize to add three rounds
bullet.add(new Object());
bullet.add(new Object());
bullet.add(new Object());
}
@Override
void move(a) {
System.err.println("Mobile");
}
@Override
void attack(a) {
System.err.println("Attack local tanks");
/ /.. Popup bullet
if(bullet.size() == 0){
System.err.println("We're out of bullets.");
return;
}
bullet.remove(bullet.get(bullet.size() -1));
}
@Override
void stop(a) {
System.err.println("Stop"); }}Copy the code
- The test class:
Unit testing is recommended. This diagram is not useful
/** * This frequent new, let us gradually become process-oriented programming... *@param args
*/
public static void main(String[] args) {
// Although we can produce tanks ourselves, we have to manually produce the corresponding tanks each time. This frequent new operation
Tank bigTank1 = new BigTank();
Tank bigTank2 = new BigTank();
Tank bigTank3 = new BigTank();
Tank bigTank4 = new BigTank();
// There are as many new objects as there are objects
Tank mouseTank1 = new MouseTank();
Tank mouseTank2 = new MouseTank();
Tank mouseTank3 = new MouseTank();
Tank mouseTank4 = new MouseTank();
// Our tanks need to be operated by ourselves
Tank myTank1 = new MyTank();
Tank myTank2 = new MyTank();
}/*/ Stop mouse tank move Mouse tank shoot Mouse tank shoot Stop Mouse tank move Mouse Tank shoot Giant tank move Giant tank shoot Mouse tank shoot Mouse Tank run out of bullets Stop Mouse tank move Mouse tank shoot Mouse tank run out of bullets Mouse tank shoot Mouse tank run out of bullets Giant tank moving Giant tank firing */
Copy the code
What’s wrong with the code above:
At first glance, there seems to be no problem. We have both an abstract superclass and a subclass to inherit from, so we can just new when we need to.
The problem is new, the first step in bad code, so to speak. Since we fell into the “detail” trap, let’s look at what went wrong with our code:
- I want to add a tank, although it can be inherited, but if we want to join the battlefield, we need to remember the new tank, and new out
- I wanted a mouse tank and accidentally created a normal local tank, which is fine when there is less code, but if there is more code, it takes a lot of time to find it
- Our test class is in control. It’s too heavy to do, not just new, but everything that happens after New.
Improved with simple Factory pattern:
Now that we know what the problem is, can we add a simple factory class to manage the tank creation process
Add the factory class tankFactory.java
Use the factory to manage the specific tank creation process:
** * tank factory, specializing in the production of tanks **@author zxd
* @version 1.0
* @date2021/1/25 * /
public class TankFactory {
/** * Create tank *@return* /
public Tank createTank(String check){
Tank tank = null;
if(Objects.equals(check, "my")){
tank = new MyTank();
}else if(Objects.equals(check, "mouse")){
tank = new MouseTank();
}else if (Objects.equals(check, "big")){
tank = new BigTank();
}else {
throw new UnsupportedOperationException("Current tanks are not supported for production");
}
returntank; }}Copy the code
Let’s rewrite the unit tests:
/** * unit test **@author zxd
* @version 1.0
* @date2021/1/25 "* /
public class Main {
/** * We handed over all the production of tanks to the factory. * 1. The creation process is gone, although it is a simple new, but the new process is given to the factory * 2. If we want to add something else to the tank later, we just need to change the factory class and the concrete implementation class, we don't need this code * 3. If the operation is not supported, the factory can also notify us that it is not doing right *@param args
*/
public static void main(String[] args) {
TankFactory tankFactory = new TankFactory();
Tank my = tankFactory.createTank("my");
Tank mouse = tankFactory.createTank("mouse");
Tank big = tankFactory.createTank("big");
// I want an undesigned tank
Tank mybig = tankFactory.createTank("mybig");
}/ * / / run results: Exception in the thread "main" mobile giant tanks move mouse Mouse tanks giant tanks to shoot Mouse tanks shot and Java. Lang. UnsupportedOperationException: Tank does not support the current production at com. Headfirst. Factory. Use. TankFactory. CreateTank ats (TankFactory. Java: 27) com.headfirst.factory.use.Main.main(Main.java:33) */
}
Copy the code
What are the changes after improvement?
- First, we leave the creation process to the factory and don’t need to focus on the details of the creation
- If you need to change the creation process, you don’t need to change the client code, just the factory code
- Again, the extension simply inherits the factory’s production abstractions.
Simple Factory pattern in Spring:
The @Bean annotation allows us to define the Bean creation process in a Spring-managed object. This class is like a factory, and the object creation details are encapsulated in specific methods. This approach is also a singleton design pattern. Spring annotations can be used for injection where needed without the need for your own new objects.
Conclusion:
The case may not be very appropriate, because there is only one new method that does not need to use the factory model, but here is the case that I think can be most associated with after thinking, so I use the tank example as the main body of the article.