From https://javadoop.com/post/design-pattern
Behavioral pattern
- The strategy pattern
- Observer model
- Chain of Responsibility model
- Template method pattern
- The state pattern
- Summary of behavioral patterns *
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
Behavioral pattern
Behavioral patterns focus on the interactions between classes, separating responsibilities and making our code clearer.
The strategy pattern
The policy pattern is all too common, so I’ll cover it first. It’s a little bit easier, so I’m going to cut to the chase and go straight to the code.
The following scenario is designed so that we need to draw a graph, and the alternative strategy is to draw with a red pen, a green pen, or a blue pen.
First, define a policy interface:
public interface Strategy {
public void draw(int radius, int x, int y);
}
Copy the code
Then we define several specific strategies:
public class RedPen implements Strategy { @Override public void draw(int radius, int x, Int y) {system.out.println (" radius:" + radius + ", x:" + x + ", y:" + y); } } public class GreenPen implements Strategy { @Override public void draw(int radius, int x, Int y) {system.out.println (" radius:" + radius + ", x:" + x + ", y:" + y); } } public class BluePen implements Strategy { @Override public void draw(int radius, int x, Int y) {system.out.println (" radius:" + radius + ", x:" + x + ", y:" + y); }}Copy the code
Classes that use policies:
public class Context { private Strategy strategy; public Context(Strategy strategy){ this.strategy = strategy; } public int executeDraw(int radius, int x, int y){ return strategy.draw(radius, x, y); }}Copy the code
Client demo:
public static void main(String[] args) { Context context = new Context(new BluePen()); // Use green pen to draw context.executeDraw(10, 0, 0); }Copy the code
Put it on a picture so you can see it clearly:
Re-upload cancelled
At this time, do you think of the bridge mode in the structural mode, they are very similar, let me bring the diagram of the bridge mode for you to compare:
If YOU ask me, they are very similar, with the bridge mode adding a layer of abstraction on the left. The bridge model has lower coupling and a more complex structure.
Observer model
The observer model is pretty simple for us. There are just two operations: observers subscribe to topics they care about and they are notified of any changes to the topic.
First, we need to define topics, and each topic needs to hold a reference to the list of observers that can be used to notify individual observers when data changes:
public class Subject { private List<Observer> observers = new ArrayList<Observer>(); private int state; public int getState() { return state; } public void setState(int state) { this.state = state; // Data has changed, notifyAllObservers(); } public void attach(Observer observer){ observers.add(observer); } public void notifyAllObservers(){for (Observer Observer: observers) {observer.update(); }}}Copy the code
Define the observer interface:
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
Copy the code
In fact, if there is only one observer class, the interface does not need to be defined. However, in most scenarios, since the observer mode is used, we expect an event to come out, and there will be multiple different classes that need to handle the corresponding information. For example, we want the classes that send SMS messages to be notified, the classes that send emails to be notified, the classes that process logistics information to be notified, and so on.
Let’s define specific observer classes:
Public Class BinaryObserver extends Observer {// Subscribe to a topic ina constructor public BinaryObserver(Subject Subject) {this.subject = subject; // Always be careful about this.subject.attach(this); @override public void update() {String result = integer.tobinaryString (subject.getState()); System.out.println(" Subscribed data changes, new data is processed as binary value: "+ result); } } public class HexaObserver extends Observer { public HexaObserver(Subject subject) { this.subject = subject; this.subject.attach(this); } @Override public void update() { String result = Integer.toHexString(subject.getState()).toUpperCase(); System.out.println(" Subscribed data changed, new data processed for hexadecimal value: "+ result); }}Copy the code
The client side is also very simple:
Public static void main(String[] args) {Subject subject1 = new Subject(); // Define the observer new BinaryObserver(Subject1); new HexaObserver(subject1); // Simulate data changes, in which case the observer's update method will be called subject.setstate (11); }Copy the code
output:
The subscribed data is changed and the new data is processed as the binary value: 1011 The subscribed data is changed and the new data is processed as the hexadecimal value: BCopy the code
The JDK provides similar support for java.util.Observable and java.util.Observer classes.
In the actual production process, the observer mode is often implemented by messaging-oriented middleware. In order to realize the stand-alone observer mode, the author suggests readers to use EventBus in Guava, which has synchronous and asynchronous implementations. This article mainly introduces the design mode and will not expand on it.
Chain of Responsibility model
The responsibility chain usually starts with a one-way list, and then the caller only needs to call the header node, which then flows automatically. For example, process approval is a good example. As long as the end user submits the application, a chain of responsibility is automatically established according to the content information of the application, and then the flow can start.
There is a scenario where users can receive prizes for participating in an activity, but the activity requires a lot of rules verification before release, such as checking whether the user is a new user, whether there is a quota for the number of participants today, whether there is a quota for the total number of participants and so on. After setting the rules are passed, the user can claim the prize.
If the product gives you this requirement, I’m sure most people will start with a List of all the rules and then Foreach will execute each rule. But wait, reader. How does the chain of responsibility model differ from this one?
First, we define the base class of the nodes on the process:
Public abstract class RuleHandler {// Protected RuleHandler succeeded; public abstract void apply(Context context); public void setSuccessor(RuleHandler successor) { this.successor = successor; } public RuleHandler getSuccessor() { return successor; }}Copy the code
Next, we need to define each node in detail.
Verify whether the user is a new user:
public class NewUserRuleHandler extends RuleHandler { public void apply(Context context) { if (context.isNewUser()) { // Succeeding, if there are any, if (this.getsucceeded ()! = null) { this.getSuccessor().apply(context); }} else {throw new RuntimeException(" This activity is for new users only "); }}}Copy the code
Verify whether the user’s region can participate:
public class LocationRuleHandler extends RuleHandler { public void apply(Context context) { boolean allowed = activityService.isSupportedLocation(context.getLocation); if (allowed) { if (this.getSuccessor() ! = null) { this.getSuccessor().apply(context); }} else {throw new RuntimeException(" Sorry, your region is unable to participate in this event "); }}}Copy the code
Check whether the prize has been claimed:
public class LimitRuleHandler extends RuleHandler { public void apply(Context context) { int remainedTimes = activityService.queryRemainedTimes(context); If (remainedTimes > 0) {if (this.getsucceeded ()! = null) { this.getSuccessor().apply(userInfo); }} else {throw new RuntimeException(" You're too late, the prize is gone "); }}}Copy the code
Client:
public static void main(String[] args) { RuleHandler newUserHandler = new NewUserRuleHandler(); RuleHandler locationHandler = new LocationRuleHandler(); RuleHandler limitHandler = new LimitRuleHandler(); / / assume the event only calibration area and number of prizes, not check the new and old users locationHandler setSuccessor (limitHandler); locationHandler.apply(context); }Copy the code
The code is actually very simple, is to define a linked list, and then pass through any node, if this node has a successor node, then pass on.
I’ll leave it to the reader to figure out how this differs from our previous example of using a List to store rules that need to be executed.
Template method pattern
The template method pattern is very common in code with inheritance structures, and is used heavily in open source code.
There is usually an abstract class:
Public abstract class AbstractTemplate {public void templateMethod(){init(); apply(); End (); } protected void init() {system.out.println (" Init abstraction is implemented, subclasses can also override "); } // Leave it to subclasses to implement protected abstract void apply(); protected void end() { } }Copy the code
The template method calls three methods, of which apply() is an abstract method and subclasses must implement it. In fact, there are several abstract methods in the template method that are completely free. We can also set all three methods as abstract methods and let subclasses implement them. That is, the template method is only responsible for defining what should be done in step 1, what should be done in step 2, and what should be done in step 3. It is up to the subclasses to do that.
Let’s write an implementation class:
Public class ConcreteTemplate extends AbstractTemplate {public void apply() {system.out.println (" ConcreteTemplate extends AbstractTemplate "); } public void end() {system.out.println (" we can use method3 as a hook method, just overwrite it as needed "); }}Copy the code
Client call demo:
public static void main(String[] args) { AbstractTemplate t = new ConcreteTemplate(); // Call the template method t.templatemethod (); }Copy the code
Code is actually very simple, basically see understand, the key is to learn to use their own code.
The state pattern
I’ll cut the crap, but let’s do a simple example. One of the most basic needs of commodity inventory centers is destocking and replenishment. Let’s see how to write it in state mode.
And the point is, instead of focusing on the Context is what operations are going to be done, we’re focusing on what operations are going to be done in this Context.
Define the status interface:
public interface State {
public void doAction(Context context);
}
Copy the code
Define the state of inventory reduction:
Public class DeductState implements State {public void doAction(Context Context) {system.out.println (" DeductState implements State "); context.setState(this); / /... } public String toString(){return "nt State"; }}Copy the code
Define replenishment inventory state:
Public class RevertState implements State {public void doAction(Context Context) {system.out.println (" implements this item "); context.setState(this); / /... } public String toString() {return "Revert State"; }}Copy the code
Using context.setState(this), let’s see how we define the context class:
public class Context { private State state; private String name; public Context(String name) { this.name = name; } public void setState(State state) { this.state = state; } public void getState() { return this.state; }}Copy the code
Let’s take a look at client calls to make it crystal clear:
Public static void main(String[] args) {// wE need to use iPhone X Context Context = new Context("iPhone X"); State revertState = new revertState (); revertState.doAction(context); // Similarly, deductState = new deductState (); deductState.doAction(context); // Context.getState ().toString(); }Copy the code
As the reader might have noticed, in the example above, if we didn’t care what state the context was in, the context wouldn’t have to maintain the state property, and the code would have been much simpler.
But after all, the inventory of goods example is only one example, and there are many more examples where we need to know what state the context is in.
Summary of behavioral patterns
The behavioral pattern section introduces the policy pattern, observer pattern, chain of responsibility pattern, template method pattern, and state pattern. In fact, the classic behavioral pattern also includes memo pattern, command pattern, etc., but their use scenarios are limited, and this article is too large, I will not introduce them.
conclusion
The goal of learning design patterns is to make our code more elegant, maintainable, and extensible. Sorting out this article, LET me re-examine each design mode, for my own harvest is quite big. I think, the biggest beneficiary of the article is generally the author himself, in order to write an article, need to consolidate their knowledge, need to find all kinds of information, and, their own writing is the easiest to remember, is also my advice to readers.
(Full text)
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
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
This article is published by OpenWrite!