Boss: **, first look at this task, first think about how to do, and then submit a document to me, remember to use design patterns, don’t write something useless, you know which design patterns should be used! I:
The task is to use design patterns, so let’s first understand what design patterns are, what they are divided into, what each design pattern has, what are the advantages and disadvantages of it!
1. What are design patterns?
In short, Design pattern is the solution to the general problems faced by software developers in the process of software development. These solutions have been developed by many software developers through trial and error over a long period of time, and then come up with some better solutions that you can learn to use.
2. Types of design patterns
1. Creation patterns: These design patterns provide a way to hide creation logic while creating objects, rather than instantiating objects directly using the new operator. This gives the program more flexibility in deciding which objects to create for a given instance, including:
- Factory Pattern
- Abstract Factory Pattern
- Singleton Pattern
- Builder Pattern
- Prototype Pattern
Structural patterns: These design patterns focus on combinations of classes and objects. The concept of inheritance is used to combine interfaces and define how composite objects gain new functionality, including:
- Adapter Pattern
- Bridge Pattern
- Filter Pattern (Filter, Criteria Pattern)
- Composite Pattern
- Decorator Pattern
- Facade Pattern
- Flyweight Pattern
- Proxy Pattern
3. Behavioral patterns: These design patterns pay special attention to communication between objects, including:
- Chain of Responsibility Pattern
- Command Pattern
- Interpreter Pattern
- Iterator Pattern
- Mediator Pattern
- Memento Pattern
- Observer Pattern
- State Pattern
- Null Object Pattern
- Strategy Pattern
- Template Pattern
- Visitor Pattern
4. J2EE patterns: These design patterns focus specifically on the presentation layer. These patterns, identified by Sun Java Center, include:
- MVC Pattern (MVC Pattern)
- Business Delegate Pattern
- Composite Entity Pattern
- Data Access Object Pattern
- Front Controller Pattern
- Intercepting Filter Pattern
- Service Locator Pattern
- Transfer Object Pattern
3. Six principles of design patterns
-
The Open Close Principle: Open for extensions, closed for modifications. When the program needs to be extended, you cannot modify the original code to achieve a hot plug effect. In short, to make the program extensible, easy to maintain and upgrade. To achieve this, we need to use interfaces and abstract classes, which will be discussed later in the concrete design.
-
Liskov Substitution Principle: The Richter Substitution Principle is one of the basic tenets of object-oriented design. Richter’s rule of substitution says that wherever a base class can appear, a subclass must appear. LSP is the cornerstone of inheritance reuse. The base class can be reused only when the derived class can replace the base class and the function of the software unit is not affected. The derived class can also add new behaviors on the base class. Richter’s substitution principle is a complement to the open – close principle. Abstract is the key step to realize the open and close principle, and the inheritance relationship between base class and subclass is the concrete realization of abstraction, so the Richter substitution principle is the specification of the concrete steps to realize abstraction.
-
Dependence Inversion Principle: this Principle is the basis of the open and close Principle. It is for interface programming that relies on abstraction rather than on concrete.
-
Interface Segregation Principle: This Principle means that it is better to use multiple segregated interfaces than to use a single Interface. It also has another meaning: reducing coupling between classes. Thus, in fact, design mode is a software design idea that starts from large-scale software architecture and is easy to upgrade and maintain. It emphasizes reducing dependence and coupling.
-
Demeter Principle: The least known Principle means that an entity should interact with other entities as little as possible, so that the functional modules of the system are relatively independent.
-
The Composite Reuse Principle: Use Composite/aggregate rather than inheritance when possible.
4. Introduction and implementation of various design modes
Because there is such a wide variety of design patterns, here introduce a factory pattern only, the abstract factory pattern, singleton pattern and strategy pattern, original combination patterns should also write down, but found some flaws, I look again, added later, if you want to learn other, please click the link design pattern | novice tutorial to check!
1. Factory mode
Advantages: 1. A caller who wants to create an object needs only to know its name. 2, high scalability, if you want to add a product, just extend a factory class can. 3, shield the concrete implementation of the product, the caller only cares about the interface of the product.
Disadvantages: 1. Every time a product is added, a concrete class and object realization factory need to be added, doubling the number of classes in the system, increasing the complexity of the system to a certain extent, but also increasing the dependence of concrete classes in the system. This is not a good thing.
Usage scenarios: 1. Log logger: Records may be recorded to the local hard disk, system events, remote servers, and so on. Users can choose where to record logs. 2. Database access, when the user does not know which kind of database is used in the final system, and the database may change. 3, design a connection server framework, need three protocols, “POP3”, “IMAP”, “HTTP”, can take these three as a product class, common implementation of an interface.
Notes: 1. As a class creation pattern, the factory method pattern can be used anywhere complex objects need to be generated. It is important to note that complex objects are suitable for factory patterns, while simple objects, especially those that can be created by simply using New, do not need factory patterns. If you use the factory pattern, you need to introduce a factory class, which adds complexity to the system.
To realize the DEMO:
(1) Create an interface
public interface Color {
void according(a);
}
Copy the code
(2) Create the implementation class of the interface
public class Blue implements Color {
@Override
public void according(a) {
System.out.println("I am blue."); }}Copy the code
public class Red implements Color {
@Override
public void according(a) {
System.out.println("I am red."); }}Copy the code
(3) Create a factory that generates objects based on the entity class given the information
public class ColorFactory {
public Color getColor(String type){
if (StringUtils.isEmpty(type)){
return null;
}
if (type.equals("RED")) {return new Red();
}
if (type.equals("BLUE")) {return new Blue();
}
return null; }}Copy the code
(4) Use this factory to get the object of the entity class by passing type information
public class ColorFactoryDemo {
public static void main(String[] args) {
ColorFactory colorFactory = new ColorFactory();
// Get the blue class and call the method
Color blue = colorFactory.getColor("BLUE");
blue.according();
// Get the red class call method
Color red = colorFactory.getColor("RED"); red.according(); }}Copy the code
(5) View the results
2. Abstract factory pattern
Advantages: 1. When multiple objects in a product family are designed to work together, it ensures that the client always uses only objects in the same product family.
Disadvantages: 1. It is very difficult to expand the product family. If you want to add a certain product of a series, you need to add codes in both the abstract Creator and the concrete one.
Use scenario: 1, QQ change skin, a set of change together. 2. Generate programs for different operating systems.
Notes: 1. Product family is difficult to expand, and product grade is easy to expand.
To realize the DEMO:
(1) Create a shape interface
public interface Shape {
void draw(a);
}
Copy the code
(2) Create a shape interface implementation class
public class Rectangle implements Shape {
@Override
public void draw(a) {
System.out.println("Rectangle"); }}Copy the code
public class Square implements Shape {
@Override
public void draw(a) {
System.out.println("Square"); }}Copy the code
(3) Create a color interface
public interface Color {
void fill(a);
}
Copy the code
(4) Create a color interface implementation class
public class Red implements Color {
@Override
public void fill(a) {
System.out.println("Red"); }}Copy the code
public class Blue implements Color {
@Override
public void fill(a) {
System.out.println("Blue"); }}Copy the code
(5) Create abstract classes for Color and Shape objects to get factories
public interface AbstractFactory {
/** * get the color **@param color
* @return* /
public abstract Color getColor(String color);
/** * get the shape **@param shape
* @return* /
public abstract Shape getShape(String shape);
}
Copy the code
(6) Create abstract classes for Color and Shape objects to get factories
public abstract class AbstractFactory {
/** * get the color **@param color
* @return* /
public abstract Color getColor(String color);
/** * get the shape **@param shape
* @return* /
public abstract Shape getShape(String shape);
}
Copy the code
(7) Create a factory class that extends AbstractFactory to generate objects of the entity class based on the given information
public class ShapeFactory extends AbstractFactory {
@Override
public Color getColor(String color) {
return null;
}
@Override
public Shape getShape(String shape) {
if (StringUtils.isEmpty(shape)){
return null;
}
if (shape.equalsIgnoreCase("SQUARE")) {return new Square();
}
if (shape.equalsIgnoreCase("RECTANGLE")) {return new Rectangle();
}
return null; }}Copy the code
public class ColorFactory extends AbstractFactory {
@Override
public Color getColor(String color) {
if (StringUtils.isEmpty(color)){
return null;
}
if (color.equalsIgnoreCase("RED")) {return new Red();
}
if (color.equalsIgnoreCase("BLUE")) {return new Blue();
}
return null;
}
@Override
public Shape getShape(String shape) {
return null; }}Copy the code
(8) Create a factory creator/generator class to retrieve factories by passing shape or color information
public class FactoryProducer {
public static AbstractFactory getFactory(String type){
if (StringUtils.isEmpty(type)){
return null;
}
if (type.equalsIgnoreCase("SHAPE")) {return new ShapeFactory();
}else if (type.equalsIgnoreCase("COLOR")) {return new ColorFactory();
}
return null; }}Copy the code
(9) Use FactoryProducer to get AbstractFactory, passing type information to get objects of entity class
public class AbstractFactoryPatternDemo {
public static void main(String[] args){
// Get the shape factory
AbstractFactory shape = FactoryProducer.getFactory("SHAPE");
// Get the color factory
AbstractFactory color = FactoryProducer.getFactory("COLOR");
// Get an object with a Rectangle shape
Shape rectangle = shape.getShape("RECTANGLE");
rectangle.draw();
// Get the object with the shape Square
Shape square = shape.getShape("SQUARE");
square.draw();
// Get the object with the color Red
Color red = color.getColor("RED");
red.fill();
// Get the object with the color Blue
Color blue = color.getColor("BLUE"); blue.fill(); }}Copy the code
(10) Run and view the result
3. Singleton mode
Advantages: 1, there is only one instance in memory, reducing the overhead of memory, especially frequent creation and destruction of instances (such as the school of management home page cache). 2, avoid multiple occupation of resources (such as write file operations).
Disadvantages: 1, no interface, no inheritance, conflict with the single responsibility principle, a class should only care about internal logic, not how to instantiate outside.
Usage scenarios: 1, the production of a unique serial number. 2. The counters in the WEB are not added to the database every time they are refreshed, but cached with singletons first. 3. Creating an object consumes too many resources, such as I/O connections to the database.
Note: 1. Synchronized (singleton.class) must be used in getInstance() to prevent instance instantiation caused by multiple threads entering simultaneously.
To realize the DEMO:
(1) Create a Singleton class and provide methods to obtain objects
public class SingleObject {
// Create an object for SingleObject
private static SingleObject singleObject = new SingleObject();
// Make the constructor private so that the class is not instantiated
private SingleObject(a){}
// Get the only available object
public static SingleObject getInstance(a){
return singleObject;
}
public void showMessage(a){
System.out.println("Hello SingleObject!"); }}Copy the code
(2) Get the unique object from the Singleton class and call the method
public class SingleObjectDemo {
public static void main(String[] args) {
// Invalid constructor
// Compile-time error: constructor SingleObject() is not visible
//SingleObject object = new SingleObject();
// Get the only available object
SingleObject object = SingleObject.getInstance();
// Displays the messageobject.showMessage(); }}Copy the code
(3) View the results
Several implementations of singleton pattern
(1) Lazy, thread is not safe
public class LazyThreadNotSafety {
private static LazyThreadNotSafety lazyThreadSafety;
private LazyThreadNotSafety(a){}public static LazyThreadNotSafety getInstance(a){
if (lazyThreadSafety == null){
lazyThreadSafety = new LazyThreadNotSafety();
}
returnlazyThreadSafety; }}Copy the code
Description: This is the most basic implementation, the biggest problem with this implementation is that it does not support multi-threading. Synchronized is not strictly a singleton mode because it is not locked. This approach to lazy loading is obviously not thread-safe and does not work well in multiple threads.
(2) lazy, thread safety
public class LazyThreadSafety {
private static LazyThreadSafety lazyThreadSafety;
private LazyThreadSafety(a){}public static synchronized LazyThreadSafety getInstance(a){
if (lazyThreadSafety == null){
lazyThreadSafety = new LazyThreadSafety();
}
returnlazyThreadSafety; }}Copy the code
Description: This approach is very lazy and can work well in multiple threads, but it is inefficient and does not require synchronization 99% of the time. Advantages: Initialization is performed only on the first call, avoiding memory waste. Disadvantages: synchronized must be added to ensure singletons, but locking will affect efficiency. The performance of getInstance() is not critical to the application (this method is used infrequently).
(3) the hungry
public class HungryType {
private static HungryType hungryType = new HungryType();
private HungryType(a){}public static HungryType getInstance(a){
returnhungryType; }}Copy the code
Description: This method is more common, but prone to generate garbage objects. Advantages: Without locking, the execution efficiency will be improved. Disadvantages: Class initialization on load, waste of memory.
(4) Double check lock/double check lock
public class DoubleLock {
private volatile static DoubleLock doubleLock;
private DoubleLock(a){}public static DoubleLock getInstance(a){
if (doubleLock == null) {synchronized (DoubleLock.class){
if (doubleLock == null){
doubleLock = newDoubleLock(); }}}returndoubleLock; }}Copy the code
Description: This method uses double lock mechanism, safe and can maintain high performance in the case of multi-threading.
(5) Registered/static inner class
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (a){}
public static final Singleton getInstance(a) {
returnSingletonHolder.INSTANCE; }}Copy the code
Description: This method can achieve the same effect of double lock mode, but the implementation is simpler. Lazy initialization for static fields should be used instead of double-checking. This approach is only suitable for static domains, and double-check can be used when the instance domain requires delayed initialization.
(6)
public enum Singleton {
INSTANCE;
public void whateverMethod(a) {}}Copy the code
Description: This implementation is not yet widely adopted, but it is the best way to implement the singleton pattern. It is more concise, automatically supports serialization mechanisms, and absolutely prevents multiple instantiations.
As a rule of thumb: in general, it is not recommended to use lazy ways 1 and 2. It is recommended to use hungry ways 3. The fifth registration method is used only when the lazy loading effect is explicitly implemented. If it comes to deserializing and creating objects, you can try the sixth method of enumeration. If you have other special requirements, you can consider using a fourth double-check mode.
4. Strategy mode
Advantages: 1, the algorithm can be freely switched. 2. Avoid using multiple conditional judgments. 3. Good expansibility.
Disadvantages: 1. The number of policy classes will increase. 2. All policy classes need to be exposed.
Usage scenarios: 1. If there are many classes in a system that are distinguished only by their behavior, then using the policy pattern can dynamically let an object choose one behavior among many behaviors. A system needs to dynamically choose one of several algorithms. 3. If an object has many behaviors, these behaviors can be implemented using multiple conditional selection statements without appropriate patterns.
Note: If a system has more than four policies, you need to consider using mixed mode to solve the problem of policy class inflation.
To realize the DEMO:
(1) Create the interface that the policy class should implement
public interface Strategy {
public int doOperation(int num1, int num2);
}
Copy the code
(2) Create three policy classes
@Service
public class StrategyAdd implements Strategy {
@Override
public int doOperation(int num1, int num2) {
returnnum1 + num2; }}Copy the code
@Service
public class StrategyReduction implements Strategy {
@Override
public int doOperation(int num1, int num2) {
returnnum1 - num2; }}Copy the code
@Service
public class StrategyTake implements Strategy {
@Override
public int doOperation(int num1, int num2) {
returnnum1 * num2; }}Copy the code
(3) Create Context class
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
returnstrategy.doOperation(num1, num2); }}Copy the code
(4) Create a policy enumeration class
public enum StrategyEnum {
ADD("strategyAdd"),
SUB("strategyReduction"),
TAKE("strategyTake");
/ * * description * /
private String clazz;
private StrategyEnum(String clazz) {
this.clazz = clazz;
}
public String getClazz(a) {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
public static Map<String, String> toMap(a) {
Map<String, String> enumMap = new HashMap<String, String>();
StrategyEnum[] ary = StrategyEnum.values();
for (int i = 0, size = ary.length; i < size; i++) {
enumMap.put(ary[i].name(), ary[i].getClazz());
}
returnenumMap; }}Copy the code
(5) Create SpringContextUtils class
@Component
public class SpringContextUtils implements ApplicationContextAware {
public static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
}
public static Object getBean(String name) {
return applicationContext.getBean(name);
}
public static <T> T getBean(String name, Class<T> requiredType) {
return applicationContext.getBean(name, requiredType);
}
public static boolean containsBean(String name) {
return applicationContext.containsBean(name);
}
public static boolean isSingleton(String name) {
return applicationContext.isSingleton(name);
}
public static Class<? extends Object> getType(String name) {
returnapplicationContext.getType(name); }}Copy the code
(6) Use StrategyDemo to implement CommandLineRunner interface to implement the run method after startup to see the behavior change when it changes the Strategy.
@Component
public class StrategyDemo implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// Get the corresponding processing type in the enumeration class
String clazzName = getClassName("ADD");
// Get the class by reflection
Object strategyAdd = SpringContextUtils.getBean("strategyAdd");
Strategy bean = SpringContextUtils.getBean(clazzName, Strategy.class);
Context context = new Context(bean);
System.out.println("10 + 5 =" + context.executeStrategy(10.5));
// Get the corresponding processing type in the enumeration class
String clazzName1 = getClassName("SUB");
// Get the class by reflection
Strategy bean1 = SpringContextUtils.getBean(clazzName1, Strategy.class);
context = new Context(bean1);
System.out.println("10-5 =" + context.executeStrategy(10.5));
// Get the corresponding processing type in the enumeration class
String clazzName2 = getClassName("TAKE");
// Get the class by reflection
Strategy bean2 = SpringContextUtils.getBean(clazzName2, Strategy.class);
context = new Context(bean2);
System.out.println("10 times 5 is equal to" + context.executeStrategy(10.5));
}
/** * Gets the corresponding policy class **@param type
* @return* /
private static String getClassName(String type) {
String clazzName = "";
Map<String, String> maps = StrategyEnum.toMap();
for (Map.Entry<String, String> entry : maps.entrySet()) {
if (entry.getKey().equalsIgnoreCase(type)) {
clazzName = entry.getValue();
break; }}returnclazzName; }}Copy the code
(7) View the results
The strategic pattern has several important points:
1, enumeration of each strategy must first letter, otherwise SpringContextUtils. GetBean is obtained. 2. The policy class must add @service and must be injected into the Spring container, otherwise it cannot be reflected to get the instance. 3, SpringContextUtils class is the reflection of the corresponding instance, and SpringContextUtils is the implementation of the ApplicationContextAware interface, after starting the SpringBoot project, The setApplicationContext method is used to inject instances of ApplicationContext, so you can’t use the main method to start directly, in which case instances of ApplicationContext cannot be injected. The StrategyDemo class must instantiate this class to the Spring container with the @Component annotation, otherwise the project will not be able to call the run method at startup!
There are many design patterns, is a large number of software developers after a long period of trial and error summed up, we can go to the rookie tutorial have a look, encountered in the project can be applied, this article will be added later, today first write here, thank you!
If there is a need you can pay attention to my public number, will immediately update the Java related technical articles, public number there are some practical information, such as Java second kill system video tutorial, dark horse 2019 teaching materials (IDEA version), BAT interview summary (classification complete), MAC commonly used installation package (some are taobao to buy, Has PJ’s).