Java Design Pattern
From https://javadoop.com/post/design-pattern
This series of articles will be organized into my GitHub repository for the Java Interview Guide. Check out my repository for more highlights
https://github.com/h2pl/Java-Tutorial
Click Star, fork if you like
The article will also be published on my personal blog for a better reading experience:
www.how2playlife.com
WeChat public is this article number of the Java technology river’s lake 】 【 the laying solid foundation of Java series post one, part of the content is derived from the network, this paper in order to put this article speaks with clarity theme, also integrates a lot I think good technology content, reference some good blog posts, if there are any copyright infringement, please contact the author. This series of blog posts will show you how to learn the basic knowledge of Java step by step from the beginning to the advanced level, and then understand the implementation principle behind each Java knowledge point, a more complete understanding of the entire Java technology system, and form your own knowledge framework. In order to better summarize and test your learning results, this series of articles will also provide interview questions and reference answers for each point.
If you have any suggestions or questions about this series of articles, you can also follow the public account “Java Technology Lake” to contact the author. You are welcome to participate in the creation and revision of this series of blog posts
Structural mode
While the creative pattern introduced some design patterns for creating objects, the structural pattern introduced in this section aims to decouple our code by changing its structure, making our code easy to maintain and extend.
The proxy pattern
The first proxy pattern is one of the most commonly used, using a proxy to hide implementation details of a concrete implementation class and often to add some logic before and after the actual implementation.
Since it is a proxy, the real implementation is hidden from the client and the proxy takes care of all requests from the client. Of course, an agent is just an agent, it doesn’t do the actual business logic, it’s just a layer, but to the client, it must appear to be the real implementation that the client needs.
Understand the word agent, and the pattern is simple.
public interface FoodService { Food makeChicken(); Food makeNoodle(); } public class FoodServiceImpl implements FoodService { public Food makeChicken() { Food f = new Chicken() f.setChicken("1kg"); f.setSpicy("1g"); f.setSalt("3g"); return f; } public Food makeNoodle() { Food f = new Noodle(); f.setNoodle("500g"); f.setSalt("5g"); return f; Public class FoodServiceProxy implements FoodService {// There must be a real implementation class that implements FoodService. Private FoodService FoodService = new FoodServiceImpl(); Public Food makeChicken() {system.out.println (" We're about to start making chicken "); // If we define this as core code, then the core code is what the real implementation class does, and the proxy just does "unimportant" things before and after the core code. System.out.println(" Chicken done, pepper "); / / to enhance food. AddCondiment (" pepper "); return food; } public Food makeNoodle() {system.out.println (" prepare to make noodles ~"); Food food = foodService.makeNoodle(); System.out.println(" done ") return food; }}Copy the code
Client call, note that we are instantiating the interface with a proxy:
FoodService = new FoodServiceProxy(); foodService.makeChicken();Copy the code
We found no. The agency model is simply doing it“Method packaging”Or do“Method enhancement”. In aspect oriented programming, the term should not be touted; in AOP, it is the process of dynamic proxy. For example, in Spring, we don’t define our own proxy class, but Spring dynamically defines the proxy for us, and then dynamically adds the code logic we defined in @before, @After, and @around to the proxy.
Speaking of dynamic proxies, we can expand to say… If our class defines an interface, such as the UserService interface and the UserServiceImpl implementation, then use the JDK dynamic proxy. Interested readers can check out the source code for the java.lang.Reflect. Proxy class; Spring uses CGLIB for dynamic proxy, which is a JAR package with good performance.
Adapter mode
Let’s say the proxy pattern and the adapter pattern, because they are similar, and we can make a comparison here.
What the adapter pattern does is, there is an interface that needs to be implemented, but none of our existing objects meet it, and we need to add another layer of adapters to adapt it.
There are three types of adapter patterns: default adapter pattern, object adapter pattern, and class adapter pattern. Before you try to sort these out, let’s look at some examples.
Default adapter mode
First, let’s look at the simplest Adapter pattern, the Default Adapter.
We use the FileAlterationListener in the Appache Commons-io package as an example. This interface defines a number of methods for monitoring files or folders, and triggers the corresponding methods whenever an operation occurs.
public interface FileAlterationListener {
void onStart(final FileAlterationObserver observer);
void onDirectoryCreate(final File directory);
void onDirectoryChange(final File directory);
void onDirectoryDelete(final File directory);
void onFileCreate(final File file);
void onFileChange(final File file);
void onFileDelete(final File file);
void onStop(final FileAlterationObserver observer);
}
Copy the code
This interface is a big problem of abstract methods too much, if we want to use this interface, means that we need to realize each abstract method, if we want to monitor folder files to create and delete events, but we still have to implement all methods, it is clear that this is not what we want.
So, we need the following adapter, which implements the above interface, but all the methods are empty methods, so we can define our own class to inherit the following class instead.
public class FileAlterationListenerAdaptor implements FileAlterationListener {
public void onStart(final FileAlterationObserver observer) {
}
public void onDirectoryCreate(final File directory) {
}
public void onDirectoryChange(final File directory) {
}
public void onDirectoryDelete(final File directory) {
}
public void onFileCreate(final File file) {
}
public void onFileChange(final File file) {
}
public void onFileDelete(final File file) {
}
public void onStop(final FileAlterationObserver observer) {
}
}
Copy the code
For example, we can define the following class, and we only need to implement the method we want to implement:
Public class FileMonitor extends FileAlterationListenerAdaptor {public void onFileCreate (final File File) {/ / File creation doSomething(); } public void onFileDelete(final File File) {// doSomething(); }}Copy the code
Of course, this is just one of the adapter patterns, and it’s the simplest one, and it needs no further explanation. Next, the “orthodox” adapter pattern is introduced.
Object adapter pattern
Take a look at an example from Head First Design Patterns, where I modified it a little bit to see how I could adapt a chicken to a duck so that the chicken could also be used as a duck. Because we don’t have a proper implementation class for this interface, we need an adapter.
public interface Duck { public void quack(); Public void fly(); Public void gobble(); Public void fly(); // fly} public class WildCock implements Cock {public void gobble() {system.out.println (" WildCock "); } public void fly() {system.out.println (system.out.println); }}Copy the code
Duck interface has two methods: fly() and quare(). If a chicken wants to pass for a duck, the fly() method is readily available, but a chicken can’t quack like a duck. There is no quack() method. This time you need to adapt:
// Public class CockAdapter implements Duck {Cock Cock; Public CockAdapter(Cock Cock) {this. Cock = Cock; } // Override public void quack() {cock. Gobble (); } @Override public void fly() { cock.fly(); }}Copy the code
The client call is simple:
Public static void main(String[] args) {Cock wildCock = new wildCock (); Duck Duck = new CockAdapter(wildCock); . }Copy the code
At this point, you know what the adapter pattern is. We need a duck, but we only have a chicken. In this case, we need to define an adapter that acts as the duck, but the methods in the adapter are implemented by the chicken.
Let’s use a diagram to illustrate this briefly:
The diagram above should be easy to understand, so I won’t explain it any further. Now, let’s look at how the class fit pattern works.
Adapter-like pattern
Without further ado, get straight to the picture above:
As you can easily understand from this diagram, the adapter automatically acquires most of the methods it needs by inheriting methods. Target t = new SomeAdapter(); That’s it.
Adapter Pattern Summary
- Similarities and differences between class adaptation and object adaptation
> < p style = "max-width: 100%; clear: both; min-height: 1em; > > Class adaptation belongs to the static implementation, object adaptation belongs to the combination of dynamic implementation, object adaptation requires multiple instantiation of an object. > > In general, objects fit a lot.Copy the code
- Similarities and differences between the adapter pattern and the broker pattern
Comparing the two patterns is really comparing the object adapter pattern and the proxy pattern. In terms of code structure, they are very similar and both require an instance of a concrete implementation class. But their purpose is different. The proxy pattern enhances the original method. The adaptor is designed to provide "packaging a chicken into a duck and then using it as a duck", and there is no inheritance between the chicken and the duck.Copy the code
Bridge pattern
To understand the bridge pattern is to understand code abstraction and decoupling.
We first need a bridge, which is an interface that defines the interface methods provided.
public interface DrawAPI {
public void draw(int radius, int x, int y);
}
Copy the code
Then there’s a list of implementation classes:
public class RedPen implements DrawAPI { @Override public void draw(int radius, int x, Int y) {system.out.println (" radius:" + radius + ", x:" + x + ", y:" + y); } } public class GreenPen implements DrawAPI { @Override public void draw(int radius, int x, Int y) {system.out.println (" radius:" + radius + ", x:" + x + ", y:" + y); } } public class BluePen implements DrawAPI { @Override public void draw(int radius, int x, Int y) {system.out.println (" radius:" + radius + ", x:" + x + ", y:" + y); }}Copy the code
Define an abstract class whose implementation classes all need to use DrawAPI:
public abstract class Shape {
protected DrawAPI drawAPI;
protected Shape(DrawAPI drawAPI){
this.drawAPI = drawAPI;
}
public abstract void draw();
}
Copy the code
Define a subclass of an abstract class:
Public class extends Shape {private int extends Shape; public Circle(int radius, DrawAPI drawAPI) { super(drawAPI); this.radius = radius; } public void draw() { drawAPI.draw(radius, 0, 0); }} public Rectangle extends Shape {private Rectangle extends Shape; private int y; public Rectangle(int x, int y, DrawAPI drawAPI) { super(drawAPI); this.x = x; this.y = y; } public void draw() { drawAPI.draw(0, x, y); }}Copy the code
Finally, let’s look at the client demo:
public static void main(String[] args) {
Shape greenCircle = new Circle(10, new GreenPen());
Shape redRectangle = new Rectangle(4, 8, new RedPen());
greenCircle.draw();
redRectangle.draw();
}
Copy the code
In case it’s not very clear step by step, I put everything together in one picture:
So this should give you an idea of where the abstraction is and how it decouples. The advantage of the bridge pattern is also obvious: it is very easy to extend.
This section takes the example from here and modifies it.
Decorative pattern
Want to decorate pattern clearly understand, it is not an easy thing. The reader may know that several classes in Java IO are typical decorative applications, but the reader may not know the relationship between them, and may forget it after reading this section. Hopefully, the reader will have a deeper understanding of it after reading this section.
First, let’s take a look at a simple diagram. As we look at this diagram, we need to understand the following hierarchy:
ConcreteComponentA and ConcreteComponentB are already implemented in the Component interface. However, if we want to enhance the ConcreteComponentA and ConcreteComponentB implementation classes, we can use the decorator. Decorate implementation classes with concrete decorators for enhancement purposes.
The name explains the decorator briefly. Since it is decoration, it is often to add small functions, and we want to meet the need to add more than one small function. At the simplest, the proxy model can implement enhancements, but proxies are not easy to implement multiple enhancements. Of course, you can use proxies to wrap agents, but then the code is complicated.
To start with a few simple concepts, see that all concrete decorators can be used as Components because they implement all interfaces in the Component. The difference between them and the Component implementation class ConcreteComponent is that they are decorators, which means that even though they look awesome, they are just layers of decorators on the concrete implementation.
Note the mix of Component and Decorator nouns in this sentence.
Let’s take a look at an example to clarify the decorator pattern and then introduce the application of the decorator pattern in Java IO.
Recently, “Happy lemon” has become popular in the street. We divide the drinks of happy lemon into three categories: Black tea, green tea, coffee, on the basis of the three major categories, also increased a lot of taste, what kumquat lemon tea, kumquat lemon green tea, pearl mango, mango, mango pearl tea, black tea, roasted pearl, roasted pearl mango, green tea, black tea and coconut germ, caramel cocoa coffee, etc., each store has a long menu, but look closely, There’s not much in the way of ingredients, but they can make a lot of combinations, and they can make a lot of drinks that aren’t on the menu if they need them.
In this example, black tea, green tea, and coffee are the basic drinks, while others like kumquat lemons, mango, pearl, coconut, caramel, etc., are all used for decoration. Of course, in development, we can develop these classes just like stores: LemonBlackTea, LemonGreenTea, MangoBlackTea, MangoLemonGreenTea…… However, it soon became clear that this was not going to work, which led to the need to combine all the possibilities, and what if the customer needed to double the lemon in the black tea? What about three lemons? In case some pervert wants four lemons, so this is a way to get overtime.
All right, cut to the code.
First, define the beverage abstract base class:
Public abstract class Beverage {public abstract String getDescription(); Public abstract double cost(); }Copy the code
Then there are the three basic beverage implementation categories, black tea, green tea and coffee:
Public class BlackTea extends Beverage {public String getDescription() {return "BlackTea "; } public double cost() { return 10; }} public class extends Beverage {public String getDescription() {return "GreenTea "; } public double cost() { return 11; }}... // Coffee is omittedCopy the code
Define the condiment, the base class of the decorator, which must inherit from Beverage:
Public abstract class Condiment extends Beverage {}Copy the code
Then we define specific condiments such as lemons and mangoes, which belong to the Condiment category, and there is no doubt that they all need to inherit from the Condiment category:
public class Lemon extends Condiment { private Beverage bevarage; // The key here is to pass in a specific beverage, such as black or green tea that has not been decorated, or mango green tea that has been decorated, Public Lemon(Beverage bevarage) {this.bevarage = bevarage; } public String getDescription() {return bevarage.getDescription() + ", add lemon "; } public double cost() {return beverage. Cost () + 2; }} public extends Condiment {private Beverage bevarage; public Mango(Beverage bevarage) { this.bevarage = bevarage; } public String getDescription() {return bevarage.getDescription() + ", "; } public double cost() { return beverage.cost() + 3; // Add mango for 3 yuan}}... // Add a class for each condimentCopy the code
Look at the client call:
Public static void main(String[] args) {// first, we need a base Beverage, black tea, GreenTea or coffee Beverage = new GreenTea(); Beverage = new Lemon(beverage); Beverage = new Mongo(beverage); Println (beverage.getDescription() + "$" + beverage.cost()); //" green tea, add lemon, add mango "}Copy the code
If we need mango pearl double lemon black tea:
Beverage beverage = new Mongo(new Pearl(new Lemon(new Lemon(new BlackTea()))));
Copy the code
Is it sick?
Take a look at the image below for clarity:
At this point, you should have a clear decoration pattern.
Next, let’s talk about decoration patterns in Java IO. Look at some of the classes derived from InputStream below:
We know that InputStream represents the InputStream. Specific input sources can be files (FileInputStream), pipes (PipedInputStream), arrays (ByteArrayInputStream), etc. These are just like the black tea and green tea in the previous example of milk tea. Belongs to the base input stream.
FilterInputStream follows the key node of the decorator pattern, and its implementation class is a series of decorators. For example, BufferedInputStream means to decorate with buffering, which makes the input stream buffered. LineNumberInputStream stands for line number decoration, which can be obtained at operation time. DataInputStream decoration, which allows us to convert from an input stream to a Java primitive type value.
Of course, in Java IO, if we use decorators, it is not suitable for interface oriented programming, such as:
InputStream inputStream = new LineNumberInputStream(new BufferedInputStream(new FileInputStream("")));
Copy the code
As a result, InputStream still does not have the ability to read line numbers, because the method for reading line numbers is defined in the LineNumberInputStream class.
We should use it like this:
DataInputStream is = new DataInputStream(
new BufferedInputStream(
new FileInputStream("")));
Copy the code
So it’s hard to find pure code that strictly conforms to design patterns.
Facade pattern
The Facade Pattern (also known as the Facade Pattern) is used in many source code, such as SLF4J can be understood as the application of the Facade Pattern. This is a simple design pattern, so let’s go straight to the code.
First, we define an interface:
public interface Shape {
void draw();
}
Copy the code
Define several implementation classes:
public class Circle implements Shape { @Override public void draw() { System.out.println("Circle::draw()"); } } public class Rectangle implements Shape { @Override public void draw() { System.out.println("Rectangle::draw()"); }}Copy the code
Client call:
Public static void main(String[] args) {// Draw a circle circle = new circle (); circle.draw(); Rectangle = new Rectangle(); rectangle = new rectangle (); rectangle.draw(); }Copy the code
Draw a circle by instantiating a circle, draw a rectangle by instantiating a rectangle, and then call the draw() method.
Now, let’s look at how to use facade mode to make client calls more friendly.
Let’s first define a facade:
public class ShapeMaker { private Shape circle; private Shape rectangle; private Shape square; public ShapeMaker() { circle = new Circle(); rectangle = new Rectangle(); square = new Square(); } public void drawCircle(){circle.draw();} public void drawCircle(){circle. } public void drawRectangle(){ rectangle.draw(); } public void drawSquare(){ square.draw(); }}Copy the code
See how the client now calls:
public static void main(String[] args) { ShapeMaker shapeMaker = new ShapeMaker(); // Client calls are now clearer shapeMaker. DrawCircle (); shapeMaker.drawRectangle(); shapeMaker.drawSquare(); }Copy the code
The advantage of the facade pattern is that the client no longer needs to worry about which implementation class should be used for instantiation, but can simply call the methods provided by the facade, since the method names provided by the facade class are already friendly to the client.
Portfolio model
The composite pattern is used to represent data with a hierarchy, allowing consistent access to individual and composite objects.
As an example, each employee has attributes such as name, department, and salary, as well as a set of subordinate employees (although the set may be empty). The subordinate employees have the same structure as themselves, and also have attributes such as name, department, and their subordinate employees.
public class Employee { private String name; private String dept; private int salary; private List<Employee> subordinates; Public Employee(String name,String dept, int sal) {this.name = name; this.dept = dept; this.salary = sal; subordinates = new ArrayList<Employee>(); } public void add(Employee e) { subordinates.add(e); } public void remove(Employee e) { subordinates.remove(e); } public List<Employee> getSubordinates(){ return subordinates; } public String toString(){ return ("Employee :[ Name : " + name + ", dept : " + dept + ", salary :" + salary+" ]"); }}Copy the code
Typically, this class needs to define the add(node), remove(node), and getChildren() methods.
This is actually a combination of patterns, this simple pattern I will not do too much introduction, I believe that readers do not like to see me write nonsense.
The flyweight pattern
English is Flyweight Pattern, I don’t know who first translated this word, I feel this translation is really difficult to understand, let’s try to force the correlation. Flyweight means lightweight, and sharing components separately is sharing components, that is, reusing already generated objects, which is of course lightweight.
The easiest way to reuse objects is to use a HashMap to store each newly generated object. Every time you need an object, you go to the HashMap to see if it exists, and if it doesn’t, you generate a new object, and then you put that object into the HashMap.
I’m not going to show you this simple code.
Summary of structural patterns
Earlier, we talked about the broker pattern, adapter pattern, bridge pattern, decorative pattern, facade pattern, composite pattern and share pattern. Can the reader clarify these patterns separately? Do you have a clear picture or process in mind when talking about these patterns?
Method is enhanced, the proxy pattern do wrap chicken as a duck the adapter pattern is used as interface adapter, do good decoupling bridge model, decorative pattern will see come out from the name, is suitable for decoration or enhance class scene, the advantages of the facade pattern is a client does not need to care about instantiation, as long as the call need to approach, The composite mode is used to describe hierarchical data, while the meta-mode is used to cache objects that have been created in specific scenarios to improve performance.
Refer to the article
From https://javadoop.com/post/design-pattern
Wechat official account
Individual public number: programmer Huang Xiaoxi
Huang Xiaoxi, master of 985, is a Java engineer of Alibaba. She has rich experience and unique insights in self-learning programming, technical job hunting, Java learning and other aspects, hoping to help more programmers who want to engage in the Internet industry. The author focuses on the JAVA backend technology stack, and is keen to share the practical knowledge of programmers, learning experiences, job hunting tips, and self-taught programming and JAVA technology stack. Huang Xiaoxi is a slash youth, insist on learning and writing, believe in the power of lifelong learning, hope to make friends with more programmers, progress and growth together!
Original e-book: Follow the wechat public number [programmer Huang Xiaoxi] and reply [Original e-book] to receive my original e-book “Novice Programmer Training Manual: From a technical novice to an Alibaba Java Engineer, this e-book summarizes my 2-year learning path of Java, including learning methods, technical summary, job hunting experience and interview skills, and has helped many programmers get the offer they want!
Programmer 3T technology learning resources: some programmers learning technology resources gift package, after paying attention to the public account, the background reply keyword “information” can be free of routine access, including Java, python, C++, big data, machine learning, front-end, mobile terminal and other directions of technical information.
Technical public number: Java technology arena
If you want to follow my updated articles and shared dry goods in real time, you can follow my wechat public number [Java technology Lake]
This is the technical station of an Ali Java engineer. Author Huang Xiaoxi, focus on Java related technology: SSM, SpringBoot, MySQL, distributed, middleware, cluster, Linux, network, multithreading, occasionally talk about Docker, ELK, but also share technical dry goods and learning experience, committed to Java full stack development!
Essential learning resources for Java engineers: follow the public account and reply “Java” to receive free learning materials such as Java foundation, advanced, project and architect, etc. There are also popular technology learning videos such as database, distributed and micro-service, with rich content and both principle and practice. In addition, the author’s original Java learning guide, Java programmer interview guide and other dry resources will be presented