Design Pattern
Chapter 1: Is it better if the code is correct? —No errors are not necessarily good—Simple factory mode
1.1 Interview frustration
1.2 Code problems for beginners
1.3 Code Specifications
1.4 Object-Oriented Programming
All programming beginners will have this problem. When encountering problems, you intuitively use logic that can be understood by computers to describe and express the problem to be solved and the specific solution process. This is actually a computer-based way of thinking. For example, this calculator program first requires input of two numbers and operator symbols, and then judge how to operate based on the operator symbols to obtain the result. This is true in itself, but this thinking makes our program only meet the current needs. The program is not easy to maintain, expand, and reuse. Therefore, the requirements of high-quality code cannot be met.
1.5 movable type printing, object-oriented (see the book for the story)
First,To change, you only need to change the words you want to change, this is maintainable;
second,These words are not useless this time, and can be reused in later printing. This can be reused;
third,If you want to add words in this poem, you only need to engrave additional words and add them. This is extensible;
fourth,The arrangement of characters may actually be vertical or horizontal. At this time, you only need to move the movable type to meet the arrangement needs. This is good flexibility.;
Before the emergence of movable type printing, none of the above four characteristics could be met. To modify, it must be re-engraved, to add characters, to re-engraved, to rearrange, to re-engraved. After printing this book, this edition has no re-use value.
1.6 Object-oriented benefits
After learning the idea of object-oriented analytical design and programming, I began to consider reducing the coupling degree of programs through encapsulation, continuation, and polymorphism. The problem with traditional printing is that all characters are engraved on the same page, resulting in too high coupling degree. I began to use the design pattern to make the program more flexible, easy to modify, and easy to reuse.
1.7 Copy VS Reuse
Some people say that the job of a junior programmer is ctrl+c and ctrl+v. This is actually a very bad encoding habit, because when there are too many duplicate codes in your code to a certain program, it may be a disaster when it is maintained. The larger the system, the more serious the problems this method brings. There is a principle of programming, which is to use the best possible methods to avoid duplication.
1.8 Business packaging
To be precise,It is to separate business logic from interface logic, so that the coupling between them is reduced, only separation can be achieved easily maintained or expanded.
1.9 Tight coupling vs loose coupling
Addition, subtraction, multiplication and division should be separated, and modifying one of them will not affect the other, adding the algorithm will not affect other codes.
1.10 Simple factory mode
The current problem is actually how to instantiate objects. The simple factory model, that is, who should be instantiated, and whether the instantiated objects will be added in the future, such as adding root operation. This is a very easy change. You should consider using a separate class to do the process of creating instances, which is the factory class.
You need to enter the operator symbol, and the factory instantiates the appropriate object, and realizes the calculator's results by returning the parent class through polymorphism.
1.11 UML class diagram
Class diagrams are divided into three layers. The first layer shows the name of the class. If it is an abstract class, it is displayed in italics. The second layer is the characteristics of the class, usually fields and properties, and the third layer is the operation of the class, usually methods or behaviors. Pay attention to the previous symbol, ‘+’ means public, ‘-’ means private, ‘#’ means protected
The difference between interface diagram and class diagram is mainly that there is <<interface>> at the top. The first line is the interface name and the second line is the interface method.
Inheritance relationshipuseHollow triangle △+solid line represents。
Implement the interfaceuseHollow triangle + dotted line is used to represent.
RelationshipuseSolid line arrow → to indicate. When one class ‘knows’ another class, association can be used.
Aggregation relationshipuseHollow diamond ◇+solid line arrow→To indicate. Example: Geese are flocking animals. Each geese belongs to a flock of geese, and a flock of geese can have multiple geese. Therefore, they satisfy the aggregation relationship. Aggregation represents a weak ‘own’ relationship, which reflects that object A can contain object B, but object B is not part of object A.
Synthetic relationshipuseSolid diamond + solid line arrow → to indicate. Example: Birds and wings are synthetic (combination) relationships because they are part and whole relationships, and the life cycle of wings and birds is the same. Synthesis (combination) is a strong ‘owner’ relationship, which reflects the strict relationship between part and whole, and part and whole life are the same as the same period of life.
DependenciesuseDotted arrows indicate. Example: Several major characteristics of animals, such as metabolism, can reproduce. Animals need vitality, oxygen, water, and food. That is, animals rely on oxygen and water. They are dependencies.
Programming is a technology and an art. You cannot just be satisfied with writing the code and running the results correctly. You often consider how to make the code more concise, easier to maintain, easier to expand and reuse. Only in this way can we truly improve. Writing elegant code is really a great thing.
Chapter 2 Shopping Mall Promotion-Strategic Model
2.1 Shopping mall cashier software
Make a shopping mall cashier software, and salesmen charge customers based on the unit price and quantity of goods purchased by customers.
2.2 Add discounts
Requirement 1; The mall requires that all products be held for activities, and all products are 20% off. It is also possible that there will be a 50% discount on the anniversary. There is also a promotional algorithm that may return 100 for more than 300. There may be a 50-yuan event for more than 200 yuan. Should we add subclasses? Which of these are the same and which are different?
2.3 Simple factory implementation
The discounts here are basically the same, as long as there is an initialization parameter. The number of gifts is given for a full number, two parameters are required.
Object-oriented programming does not mean that the more classes, the better. Class division is for encapsulation, but the basis of classification is abstraction, and abstract collections of objects with the same attributes and functions are classes. One-off discount and 10% off are just different in the form. After abstract analysis, all discount algorithms are the same, so discount algorithms should be the same.
Although the simple factory model can also solve this problem, this model only solves the object creation problem. Moreover, since the factory itself includes all charging methods, the mall may change the discount amount and rebate amount frequently. Each maintenance or expansion fee method changes the factory, so that the code needs to be recompiled and deployed. This is really bad, so using it is not the best way. There should be a better way to face the frequent changes in algorithms.
2.4 Policy Mode
It defines the algorithm family and encapsulates them separately so that they can be replaced with each other (these algorithms can be replaced with each other at any time, this is the point of change).This mode allows changes in the algorithm to not affect customers using the algorithm. Encapsulation changes behavior is abstract or interface.
2.5 Policy mode implementation
Normal charges, discount charges, and rebate charges are three specific strategies, which are the specific algorithms in the strategy model.
2.6 Combination of strategy and simple factory
The simple factory model requires the client to understand two classes, cashSuper and cashFactory. The usage of the policy model combined with the simple factory is that the client only needs one class cashContent, and the coupling is even lower. This completely separates the specific charging algorithm from the client.
2.7 Policy mode analysis
Reflect on the strategy model. The strategy model is a method of defining a series of algorithms. From a conceptual perspective, all of these algorithms do the same work, but the implementation is different. It can call all algorithms in the same way, reducing the coupling between various algorithm classes and the gods using algorithms.
The Strategy class hierarchy of the policy pattern defines a series of reusable algorithms or behaviors for Context.Inheritance helps to disassemble public functions in these algorithms. For discounts, rebates or other algorithms, they are actually a calculation method for charging actual products. Through inheritance, their public functions can be obtained. What is this public function??
The public function is to obtain the result of the calculation fee GetResult, which gives the algorithm an abstract parent class CashSuper.
Another advantage of strategy model is that it simplifies unit testing, because each algorithm has its own class and can be tested separately through its own interface.
Each algorithm can ensure that it is error-free, and modifying one of the algorithms will not affect the other algorithms when it is modified.
The policy mode is used to encapsulate algorithms.But in practice, we found that it can be used to encapsulate almost any type of rule, and as long as you hear during the analysis that different business rules need to be applied at different times, you can consider the possibility of using policy patterns to deal with such changes.。
In the basic policy model, the responsibility for selecting the specific implementation used is borne by the client object and transferred to the Context object of the policy model. This itself does not relieve the pressure of the client to choose judgment.After combining the strategy model with the simple factory model, the responsibility for choosing a specific implementation can also be assumed by Context, which maximizes the reduction of the client's responsibilities.
But there is another disadvantage here. If you add another algorithm, such as getting 50 for every 200, you must change the switch code in CashContext. The solution is to use reflection, (there are explanations of reflection in the abstract working mode).
Chapter 3: Shooting UFOs – The Principle of Single Responsibility
3.1 New phone
3.2 Shooting
3.3 Useless things
3.4 The principle of single responsibility
Most of the time, a product is simpler and has a single responsibility, which may be a better choice. This is the same as a principle in the design model - the principle of a single responsibility.
It can be simply understood that its accurate explanation is that in terms of a class, there should be only one reason for its change. When we are programming, we will naturally add various functions to a class. For example, when we write a form application, we will generally generate a class like Form. So we write all kinds of codes, such as some commercial operation algorithms, database access SQL statements, etc. in such classes. This means that no matter what the requirements come, you have to change the empty form class. This is actually very bad. Maintenance is troublesome, reuse is impossible, and there is also lack of flexibility.
3.5 Block game design
If a class assumes too many responsibilities, it is equivalent to coupling these responsibilities together, and a change in a responsibilities may weaken or inhibit the class's ability to complete other responsibilities. This coupling leads to fragile designs, which can suffer unexpected damage when changes occur. In fact, it is entirely possible to find out which interfaces are and which game logic are, and then separate them.
Much of what software design really does is discover responsibilities and separate those responsibilities from each other. In fact, it is not difficult to judge whether it should be shared. That is, if you can think of more than one motivation to change a class, then this class has more than one responsibilities, and you should consider the separation of responsibilities of the class.
Changes in the interface have nothing to do with the game itself. The interface is easy to change, while the game logic is not easy to change. Separating them is conducive to changes in the interface.
3.6 Are there too many mobile phone responsibilities?
In general, the development of mobile phones has its characteristics, but we need to think more about the separation of responsibilities of classes and achieve a single responsibility, so that your code can be truly easy to maintain, expand, reuse, and flexible and diverse.
Chapter 4: Both job hunting for postgraduate entrance examinations---Open-closed principle
4.1 Failed to take the postgraduate entrance examination
* and Macao return, one country, two systems thinking. In order to return to the overall situation, adding one system, one country, and two systems is a great invention in politics.In the software design model, this idea that cannot be modified but can be expanded is also the most important design principle, which is the open-closeed principle (OCP for short) or the open-closeed principle.
4.2 Open-closed principle
The open-closed principle means that software entities (classes, modules, functions, etc.) should be extended, but cannot be modified.
This principle actually has two characteristics: one is to say "open for extensions" and the other is to say "closed for changes".
When we are building any system, we should not expect that the system needs will be determined at the beginning and will never change again. This is an unrealistic and scientific idea. Since the needs will definitely change, then how to design software can be relatively easy to modify when facing changes in demand, not to mention that when new requirements come, it is to push the entire program back. What kind of design can be relatively stable in the face of changes in demand, so that the system can continuously launch new versions after the first version? Open-closed gives us answers.
The best way to design software is easy to maintain but not easy to cause problems is to expand more and modify less.
4.3 When to deal with changes
The principle of openness and closure means that when you design, you must always consider it and try to make this class good enough. Don’t modify it after it is written. If new requirements come, we will add some classes and it will be done. If the original code can remain unmoved, it will remain unmoved.
It is absolutely impossible to close the modification. No matter how "closed" the module is, there will be some changes that cannot be closed to it. Since it is impossible to completely close, the designer must make a choice about which variation should be closed by the module he designed. He must first guess the types of changes that are most likely to occur, and then construct the abstraction to isolate those changes.
It is difficult for us to guess in advance, but when small changes occur, we can find ways to deal with the possibility of greater changes as soon as possible. That is, take action immediately when the change occurs. As the saying goes, in the same place, it is not your fault to fall first, but it is your fault to fall again.
When we initially wrote the code, it is assumed that the change will not happen. When changes occur, we create abstractions to isolate similar changes that occur later. For example, the addition program written before was soon completed in a cling class, and the change has not yet occurred. Then add a subtraction function and find that adding functions requires modifying the original class, which violates the "open-closed principle" mentioned today. Therefore, we should consider reconstructing the program and adding an abstract operation to isolate specific addition, subtraction and client coupling through some object-oriented means, such as inheritance, polymorphism, etc., and the requirements can still be met and can also cope with changes. At this time, you need to add multiplication and division functions again, so you no longer need to change the client and addition and subtraction classes, but add multiplication and division subclasses. That is, in the face of requirements, changes to the program are made by adding new code, rather than changing existing code. This is the spirit of the "open-closed principle".
What we hope is to know the possible changes shortly after the development work begins. The longer it takes to find out what changes may occur, the harder it is to create the correct abstraction.
The open-closed principle is the core of object-oriented design. Following this principle can bring the huge benefits claimed by object-oriented technology, namely maintainable, scalable, reusable, and good flexibility. Developers should only abstract the parts of the program that appear frequently, however, it is also not a good idea to deliberately abstract every part of the application. Rejecting immature abstractions is as important as abstractions themselves.
4.4 Prepare both hands and do your best
Chapter 5: Can you repair computers and not radios? ——The principle of reliance on the inversion
5.1 MM requests to repair the computer
5.2 Telephone remote control computer repair
5.3 The principle of dependency reversal
PCs can be understood as large software systems. Any components such as CPU, memory, hard disk, display, etc. can be understood as classes or assemblies encapsulated in the program. Due to the easy plug-in and unplugging method of PCs, no matter which one has a problem, they can be modified or replaced without affecting other components.
It is called easy plug-in and unplugging in PCs, and this relationship is called strong cohesion and loose coupling in object-oriented.
There are only a few CPUs in the world that are produced by everyone, but they don’t know how Intel, AMD and other companies make this sophisticated little thing. This shows that the strong cohesion of the CPU is indeed strong. But it has become a product alone, and can be used by plugging it into millions of computer motherboards.
Because the CPU is standard interfaces such as pin type or contact type. This is the biggest benefit of the interface. CUP needs to define the interface, and I won’t let the outside world know that the internal complexity is, and the motherboard only needs to reserve slots with CPU pins.
There are several major object-oriented design principles mentioned here, such as the single responsibility principle mentioned before. Just like the computer repair just now, the memory is obviously broken and should not be a reason to replace the CPU. Their respective responsibilities are clear. For example, the principle of open-closing is that if the memory is not enough, you can add as long as there is enough slot. If the hard disk is not enough, you can use a mobile hard disk, etc. The interface of the PC is limited, so the expansion is limited. If the software system is well designed, it can be expanded infinitely. We have mentioned these two principles before. Here we focus on a new principle, called the principle of dependence inversion, which also translates into the principle of dependence inversion.
The principle of reliance on the inversion is that abstraction should not rely on details, details should rely on abstraction. To put it bluntly, it is necessary to program the interface, not to program the implementation. Whether the motherboard, CPU, memory, or hard disk are designed for the interface. If it is designed for implementation, the memory must correspond to a specific brand of motherboard, and then there will be an embarrassment that the memory needs to replace the motherboard.
Dependency reversal principle: A. High-level modules should not rely on low-level modules. Both should rely on abstraction. B. Abstraction should not rely on details. Details should depend on abstraction.
1. Why is it called reverse?
It is necessary to explain it carefully. Process-oriented development is that in order to make common code reusable, these commonly used codes are generally written into program libraries of many functions. In this way, when we are working on new projects, we can just call these low-level functions. For example, most of the projects we do need to access the database, so we write the code to access the database into functions, and call these functions every time we do a new project. This is also called high-level modules relying on low-level modules.
The problem lies here. When we are going to do a new project, we find that the high-level modules of business logic are the same, but customers want to use different databases or storage methods, which will cause trouble. We hope to use these high-level modules again, but the high-level modules are all bound to the low-level access database. There is no way to reuse these high-level modules, which is very bad. As I said just now, if the CPU, memory, and hard disk in a PC need to rely on the specific motherboard, once the motherboard is broken, all the components will be useless, which is obviously unreasonable. On the other hand, if the memory is broken, it should not cause other components to be useless. If they depend on abstraction regardless of high-level modules or low-level modules, they all rely on abstraction. To be specific, they are interfaces or abstract classes. As long as the interface is stable, there is no need to worry about the other changes to any one. This makes it easy to be multiplexed for both high-level and low-level modules. This is the best way.
2. Why are you not afraid of changes when relying on abstract interfaces or abstract classes?
Understand the principle of Liszcity substitution.
5.4 Lizhni substitution principle
The principle of substitution of Rich was published by Ms. Barbara Liskov in 1988. Its vernacular translation is a software entity. If it uses a parent class, it must be applicable to its subclass, and it cannot detect the difference between parent class objects and subclass objects. In other words, in software, if the parent class is replaced with its subclasses, the program's behavior has not changed. Simply put, the subtypes must be able to replace their parent types.
This seems to be a concept that you need to understand when learning inheritance. Subclasses inherit the parent class, so subclasses can appear as parent class.
Birds can fly, but penguins can’t fly. Although penguins are birds in biological classification, in the programming world, penguins cannot appear as parents - birds, because the premise is that all birds can fly, but penguins cannot fly, so penguins cannot inherit birds.
It is precisely because of this principle that inheritance and reuse is possible. Only when the subclass can replace the parent class and the functions of the software unit are not affected can the parent class be truly reused.,Subclasses can also add new behaviors based on parent class. For example, cats inherit animals and have behaviors such as eating, drinking, running, and barking as animals. But one day, we need dogs, cows, and sheep to have similar behaviors. Since they are all inherited from animals, there is no need to change the program except to change the instantiation place.
Because of the principle of Richter substitution, openness and closure are made possible.
This is OK. It is precisely because of the subtypes that the modules using parent class type can be expanded without modification. Otherwise, what else can we talk about expanding and opening up or modifying and closing down? Looking back at the principle of dependency reversal, high-level modules should not rely on low-level modules, both should rely on abstraction, and you will have a deeper understanding of this sentence.
Reliability inversion actually means that no one should rely on anyone. In addition to the agreed interface, everyone can be flexible and free.
5.5 Repair the radio
The radio is full of resistors, transistors, circuit boards and other things, all soldered together.
The radio is a typical excessive coupling. As long as the radio fails, no matter whether it is no sound, cannot adjust the frequency, or there is noise, it is difficult to repair anyway. People who don’t understand cannot repair it at all, because any problem may involve other components. The components are interdependent and difficult to maintain.Dependency reversal can actually be said to be the logo of object-oriented design. It doesn't matter which language to write a program. If you consider how to program for abstract programming rather than for details, that is, all dependencies in the program terminate in abstract classes or interfaces, that is, object-oriented design, and vice versa.
Chapter 6 What to wear is so important? ——Decorative mode
6.1 What to wear is so important?
6.2 The first edition of the side dish dressing up
Write a system that can be matched with different clothing, such as the Avatar system that is similar to QQ, online games or forums (usually used in computer games, a system that increases the number of characters' appearance by subdividing character models or images and recombining them).
It means that the personal unlocking system can be changed into various clothes and pants.Wearing clothing can be understood as an act, such as: wearing T-shirts, sneakersdirectly create a person class and add various behaviors.
6.3 Side dishes dress up second edition
Abstract a clothing category, define it as a class for wearing sneakers, inherit the clothing category, and add your own behavior to "wear sneakers"
The builder model requires that the construction process must be stable, but in our example, the construction process is unstable. For example, you can wear a suit, a jacket, a T-shirt, a cloak, a tie, and then wear a torn sneaker on the leather shoes; in other words, a person with personality can have countless solutions through the combination of clothing, which is not fixed.
We need to connect the required functions in the correct order for control.
6.4 Decorative Mode
Decorator: Dynamically adds some extra responsibilities to an object. In terms of adding functions, decorator mode is more flexible than generating subclasses.
Whether it is clothes, shoes, tie, or cloak, they can actually be understood as decoration for people.
Component defines an object interface that can dynamically add responsibilities to these objects.。
ConcreteCompoent defines a specific object (human person), and can also add some responsibilities to this object.
Decorator is a decorative abstract class that inherits Component and extends the functions of the Component class from external classes, but for Component, there is no need to know the existence of Decorator.
As for ConcreteDecorator, it is a specific decorative object (T-shirts, sneakers, etc.), which plays the role of adding responsibilities to the Component.
It turns out that the decorative mode uses SetComponent to wrap the object. In this way, the implementation of each decorative object is separated from how to use this object. Each decorative object only cares about its own functions and does not need to care about how it is added to the object chain. To use the example just now, you can wear outer pants first and then underwear, instead of having to be inside and out first.
The learning model should be good at adapting.If there is only one ConcreteComponent class and no abstract Component class, then the Decroator class can be a subclass of ContcreteComponent. By the same token, if there is only one ConcreteDecorator class, there is no need to create a separate Decorator class, but the responsibilities of Decorator and ConcreteDecorator can be merged into one class.
6.5 Side dishes dress up third edition
6.6 Decorative mode summary
Decoration mode is a way to dynamically add more features to existing features. But when exactly should I use it?
In the initial design, when the system needed new functions, new code was added to the old class. These newly added codes usually embellish the core responsibilities or main behaviors of the original class, such as decorating side dishes with suits or hip-hop clothes, but the problem with this practice is that they add new fields, new methods and new logic to the main class, thus increasing the complexity of the main class, just like the "human" you were at the beginning, and these newly added things are just to meet the needs of special behaviors that will only be performed in a certain specific situation. However, the decorative pattern provides a very good solution. It puts each function to be decorated in a separate class and allows this class to wrap the object it wants. Therefore, when special behavior needs to be performed, the client code can selectively and sequentially use the decorative function to wrap the object as needed during runtime. So the above example is presented. You can use decoration to fully arm your teeth, or you can just hang a trace of it to your underwear.
Then the advantages of the decorative mode are summarized in that the decorative functions in the class are removed from the class, which can simplify the original class. The greater advantage of this is to effectively distinguish the core responsibilities of the class from the decorative functions. And it can remove duplicate decorative logic in related classes.
Chapter 7: Making wedding dresses for others - Agent model
7.1 Make wedding dresses for others
7.2 Code without proxy
Suitors---Seekers
7.3 Only proxy code
Agent category---Seeker category: ignore the suitor. In fact, the suitor should give the suitor a gift to the suitor through the agency. This is reasonable.
Take a closer look,There are similarities between the suitor and the suitor. They all have ways to give gifts, but the gifts given by the suitor are bought by the suitor, but in fact they are given by the suitor. Since both have the same method, it means they need to implement the same interface。
7.4 Code that matches actual
Define the gift-giving interface (send flowers and chocolates), the suitor class implements the gift-giving interface, and the agent class also implements the gift-giving interface (in the implementation method, call the "Seeper" class related methods).
7.5 Agent Mode
proxy mode: Provides a proxy for other objects to control access to this object.
7.6 Agent mode application
In what occasions are the proxy mode used?
Generally speaking, it is divided into several types:
First, remote proxy, that is, provide a local representation of an object in different address spaces. This can hide the fact that an object exists in a different address space.
For example, when the application of WebService in .NET, when adding a web reference to the application project and referring to a webService, a WebReference folder and some files will be generated in the project. In fact, they are proxying, which can solve the problem of remote access by calling the proxy using the client program.
Second, virtual representative, is to create objects with high overhead as needed. It stores real objects that take a long time to instantiate. This allows for optimal performance.
For example, when opening a large HTML web page, there may be a lot of text and pictures, but you can still open it quickly. At this time, what you see is all the text, but the pictures can only be seen after downloading them one by one. Those unopened picture boxes replace real pictures through virtual agents, which store the path and size of real pictures.
Third, security agent, used to control the permissions when real objects are accessed. Generally used when an object should have different access permissions.
Fourth, intelligent guidance, refers to the agent handling other things when the real object is called.
For example, calculate the number of references to a real object, so that when the object has no reference, it can be automatically released; or load it into memory when a persistent object is referenced for the first time; or check whether it has been locked before accessing an actual object to ensure that other objects cannot change it. They all attach some housekeeping when accessing an object through the proxy.
The proxy mode is actually the indirection of introducing a certain program when accessing an object, because this indirection can be attached to multiple uses.
7.7 The scholar asked Xiao Liu to propose to him
Chapter 8 Lei Feng is still in the world - Factory Method Model
8.1 Reappearing the Living Lei Feng
8.2 Simple factory mode implementation
I have been studying factory method models, but I still don’t understand the difference between it and a simple factory. I feel that it is not as convenient as a simple factory. Why use this model? What is the essence of this model?
8.3 Factory method model implementation
First build a factory interface, then add, subtract, multiply and divide each to build a specific factory (additional factory, subtraction factory) to implement this interface.
8.4 Simple Factory VS Factory Method
As mentioned before, if you need to add other operations now, such as finding the N power of M number, or finding the N power of M number, these functions will be increased,In a simple factory, first add the function class "Find the N power of M number" and then change the factory method.Add "Case" statement to make a judgment,Now I use the factory method, and there is no problem with adding function classes. Adding related factory classes is also no problem, but if I change the client again, it does not mean that not only does not reduce the difficulty, but has added many classes and methods, which has increased the complexity? Why do you need this?
This is actually the difference between the factory method and the simple factory.The biggest advantage of the simple factory model is that the factory class contains the necessary logical judgments, and dynamically instantiates the relevant classes according to the client's selection conditions. For the client, the dependence on specific products is removed.
Just like a calculator, the client doesn't have to care about which class of instances to use, just give "+" to the factory, and the factory will automatically give the corresponding instance.The client just needs to do the calculations, and different instances will implement different operations. But the problem is also here. As you said, if you want to add a function of "finding the N power of M number", we must add the branch conditions of "Case" to the calculation factory class method and modify the original class? This is not a good idea. This means that we are not only open to expansion, but also to modifications.This violates the principle of openness and closure.
Factory Method: Define an interface for creating objects, letting the subclass decide which class to instantiate. Factory methods use instantiation of a class to delay subclasses.
We have said that since this factory class is coupled with branches, I will start with it. According to the principle of dependency reversal, we abstract the factory class into an interface. There is only one method for this interface, which is the factory method for creating abstract products. Then, all factories that want to produce specific classes will implement this interface. In this way, a factory class with a simple factory model becomes a factory abstract interface and a factory with multiple specific production objects. Therefore, when we want to add the function of "finding the N power of the number of M", we do not need to change the original factory class, we only need to add the operation class of this function and the corresponding factory class.
In this way, there are no modifications and changes in the entire factory and product system, but only expansion changes, which is completely in line with the spirit of the principle of openness and closure.
In fact, if you look closely, you will find that when implementing the factory method mode, the client needs to decide which factory to instantiate to implement the operation class. The problem of choosing judgment still exists, andThat is to say, the factory method moves the internal logical judgment of the simple factory to the client code to perform it.. If you want to add functions, you originally changed the factory class, but now you are modifying the client.
8.5 Lei Feng Factory
Lei Feng is a well-known model for doing good people and good deeds. Someone in the class is now learning to do good deeds in the name of learning from Lei Feng, and now doing good deeds on his behalf. This is actually a typical factory method application.
Lei Feng, with methods such as sweeping the floor, washing laundry, and buying rice.
College students who learn from Lei Feng, inherit Lei Feng
Then the client is implemented. If there are three people who want to do these things on their behalf, then three "college students who learn from Lei Feng" should be instantiated.
Students are all about to graduate, but helping the elderly is a long-term work, so "community volunteers" are more suitable, so a "community volunteer" category needs to be added to inherit the "Lei Feng" category.
At this time, you will find that you need to write the code of this factory at any instantiation. If there is repetition here, it will have a bad taste. Write it again in the factory method mode.
The factory method overcomes the disadvantages of simple factories that violate the open-closing principle, and maintains the advantages of the encapsulated object creation process.They all encapsulate the creation of objects in a centralized manner, so that when replacing objects, they can be implemented without major changes, reducing the coupling between client programs and product objects.. The factory method model is a further abstraction and promotion of the simple factory model. Due to the use of polymorphism, the factory method pattern maintains the advantages of a simple factory pattern and overcomes its shortcomings. But the disadvantage is that for every product you add, you need to add a product factory class, which increases the additional development volume. soThe factory method is not the best way to do it either. As mentioned before,Using "reflection" can avoid branch judgment problems。
Chapter 9 Resume Copying – Prototype Mode
9.1 Exaggerated resume
9.2 Preliminary implementation of resume code
Resume class, client calls
Three-point resumes need to be instantiated three times. If you want twenty copies, you need twenty instantiation. If you write a wrong word, you have to modify it twenty times.
9.3 Prototype Mode
Prototype mode: Use prototype instances to specify various classes to create objects, and create new objects by copying these prototypes.
The prototype pattern is actually creating another customizable object from one object, and there is no need to know any creation details.. The prototype pattern code is as follows:
Abstract prototype class (add abstract clone method and return abstract prototype class), concrete prototype class implements abstract prototype class, client code
The Cloneable interface is provided in the java class, which is the only method Clone(), so that you only need to implement this interface to complete the prototype mode.
9.4 Prototype implementation of resume
The resume class implements Cloneable interface and client calls (just call the clone method to realize the generation of a new resume, and the details of the new resume can be modified)
Using clone can improve performance. If you need to execute a constructor once every new one, you need to execute a constructor once. If the execution time of the constructor is very long, then executing this initialization operation multiple times is really inefficient. Generally, cloning is the best way to do so without changing the initialized information. This hides the details of object creation and greatly improves performance.
It is equivalent to not having to re-initialize the object, but dynamically obtaining the state of the object when it is run.
9.5 Shallow copy and deep copy
If the field is of value type, perform bit-by-bit copying on the field, and if the field is of reference type, the referenced object is copied but not the referenced object, so the original object and its copy refer to the same object. If there is an object reference in the "Resume" class, the referenced object data will not be cloned.
Shallow copy: All variables of the copied object contain the same value as the original object, while all references to other objects still point to the original object. But we may need such a requirement to copy all the objects referenced by the object to be copied.
Deep copy: Point the variables of the referenced object to the copied new object, rather than the original referenced object.
If the "resume" object refers to "work experience", "work experience" refers to "company", and "company" refers to "position"..., what should I do if one quotes one by one, one has many layers?
This is indeed a difficult question to answer. How many levels of deep copying needs to be further studied need to be considered in advance, and you must be careful when there are circular reference issues and need to be handled carefully. This is quite complicated and can be studied slowly. As for this example now, there should be no big problem, just go deep into the first level.
9.6 Deep copying of resumes
Code organization: Let work experience also implement the Cloneable interface and implement cloning methods. In the resume class, the private constructor (clone) of the work experience is called to clone the work experience data.
Since deep copy or shallow copy is often involved in some specific occasions, such as DataSet, the dataset object, has the Clone() method and Copy() method. The Clone() method is used to copy the structure of the DataSet, but does not copy the data of the DataSet, which realizes shallow copying of the original pattern. The Copy() method not only copies structure, but also copies data, but in fact it realizes deep copying of the prototype mode.
9.7 Copy resume vs. handwritten cover letter
Chapter 10: It’s useless to copy the wrong test questions - Template method mode
10.1 I can’t do multiple-choice questions, so let me know!
The biggest advantage of the exam paper is that everyone has the same questions, especially standardized exams, such as all selected or judged questions, which maximizes the performance of the answerer. Everyone has ABCD or ticks and crosses, and the result is either right or wrong. This is actually a typical design model.
10.2 Repeat = easy to make mistakes + difficult to make changes
The procedure for copying the topic is as follows:
The test paper copied by students
The test paper copied by student B
Client code: Student A = new Student A; Student B = new Student B
10.3 Refining the code
Student A and Student B have very similar test papers. Except for the different answers, there is nothing different. It is easy to write in this way and it is difficult to maintain.
The teacher will print out a test paper, print multiple copies, and ask the students to fill in the answers. Here, it should be to share the test questions and answers, abstract a parent class, let the two subclasses inherit it, and write the public test questions code into the parent class, and then you can do it.
Test paper parent code:
Parent Jin Yong's novel test paper, behavior (test questions), student subclass code = the test paper copied by student A inherits the parent category, and the test paper copied by student B inherits the parent category Behavior (the answers to each test question)
There are also the same things in the above code, such as base. Exam Question 1() and ("Answer:"). Except for the abcd option, the rest are all duplicate.
Since we have used inheritance and are sure that this inheritance is meaningful, we should become a template for subclasses. All duplicate code should be upgraded to the parent class, rather than letting each subclass be repeated.
The template method is available. When we want to complete a process or series of supervision that is consistent at a certain level of detail, but the implementation of individual steps at a more detailed level may be different, we usually consider using the template method pattern to deal with it.
The test questions are the same, only the answers are different. At this time, a virtual method of adding an answer is needed. The purpose of the virtual method is to rewrite the inherited subclasses, because everyone's answers here are different.
Subclasses only need to rewrite the virtual method and fill in the answer, and don’t worry about anything else. Because the parent class creates all duplicate templates.
The client code needs to be changed in a small way, that is, the declaration of a subclass variable was changed to a parent class, so that the polymorphism can be used to realize the reuse of the code. And this is actually a typical template method pattern.
10.4 Template method mode
TemplateMethod:Defines the skeleton of an operational algorithm and delays some steps to a subclass. The template method allows the subclass to redefine certain specific steps of the algorithm without changing the structure of the algorithm.
AbstractClass is an abstract class, in fact, it is an abstract template that defines and implements a template method. This template method is generally a concrete method, which gives a skeleton of top-level logic, and the logic composition steps are delayed to the subclass implementation in the corresponding abstract operation. Top-level logic may also call some specific methods.
ConcreteClass is a subclass that implements one or more abstract methods defined by the parent class.. Each AbstractClass may have any number of ConcreteClass corresponding to it, and each ConcreteClass can give different implementations of these abstract methods (that is, the composition steps of top-level logic), so that the implementations of top-level logic are different.
10.5 Template method mode features
The template method pattern reflects its advantages by moving the invariant behavior to the superclass and removing duplicate code from the subclass.
The template method mode provides a good code reuse platform. Sometimes, we encounter a process that consists of a series of steps that need to be performed. This process is the same at a high level, but some steps may be implemented differently. At this time, we should usually consider using the template method pattern.
When encountering this situation, when the unchanging and mutable behaviors are mixed together in the subclass implementation of the method, the unchanging behaviors will repeat in the subclass. We move these behaviors to a single place through the template method pattern, which helps the subclass get rid of the entanglement of repeated unchanging behaviors.
The template method mode is a very commonly used model. Almost everyone who plays good in inheritance and polymorphism will use it more or less in the inheritance system.For example, in the design of .NET or Java class library, the template method pattern is usually used to extract the public behavior in the class library into the abstract class.。
Note: I always think of template methods whenever there are two snippets that behave similarly but not exactly the same. Extract public processes and reusable methods to the parent class, retain different places as abstract methods, and implement them by different subclasses.
10.6 Subjective question, it depends on how you conceive
Chapter 11 Is it difficult to do things without an acquaintance? ——Dimit Principle
11.1 Go to work on the first day
I went to the IT department to get the computer and met a colleague named Xiao Zhang. As a result, when I was about to install the computer, I called him and asked him to go to a customer to deal with the PC failure immediately. The result was a whole morning. When I went to ask the Human Resources Department if I could ask others to help, the Human Resources Department asked me to find Xiao Li from the IT Department. Xiao Li took the computer form and saw that Xiao Zhang's name was written on it. So I said that Xiao Zhang was responsible for this matter and he didn't care. I have to wait for Xiao Zhang to come back before talking.
11.2 It is difficult to do things without acquaintances
The above questions are a principle of our design model. If the IT department has a supervisor who is responsible for assigning tasks and let the supervisor arrange any work that requires the IT department to cooperate, wouldn’t it be fine?
Without management, it is difficult to accomplish things by relying solely on interpersonal coordination. If the company's IT department is only Xiao Zhang, then there will be no problem. Let’s have another Xiao Li, so who should I do the job? Outsiders don’t know who is busy and who is idle. It would be even more troublesome if three people had no management staff in the IT department. As the saying goes, one monk carries water and eats it, two monks carry water and eats it, and three monks have no water.
In fact, no matter whether you recognize the IT department or not, as long as you call or find the IT department in person, they should find ways to help solve the problem. Regardless of anyone in the company, whether you know it or not, just look for the IT department.
Here, the IT department represents an abstract class or interface, and Xiao Zhang and Xiao Li represent an concrete class. Previously, they will repair the computer and not modify the dependency reversal principle mentioned in the radio, that is, interface-oriented programming, and not implementation-oriented programming. This is what it means.
11.3 Dimit principle
Dimit principle: If two classes do not have to communicate directly with each other, then the two classes should not interact directly. If one of the classes needs to call a method of another class, the call can be forwarded through a third party.
The first premise of the Dimit principle emphasizes is that in the structural design of the class, each class should try to reduce the access rights of members. That is to say, a class should package its own private state and should not be disclosed without letting other classes know about fields or behaviors.
Of course, the object-oriented design principles and the three major characteristics of object-oriented are not contradictory.The fundamental idea of the Dimit rule is to emphasize the loose coupling between classes.. Take this thing I encountered today as an example. On the first day of going to the company, how could I know the people from the IT department? If the company has good management, then the Human Resources Department should call the IT department and tell the supervisor to arrange the people and install the computer. Even if Xiao Zhang is responsible at the beginning, he will have something to do temporarily, and the supervisor can arrange for Xiao Li to deal with it. By the same token, when we are programming, the weaker the coupling between classes is, the more conducive it is to reuse. A class in weak coupling is modified and will not affect related classes. That is, the hiding of information promotes the reuse of software.
Chapter 12 Will stocks still lose money in a bull market? —— Appearance mode
12.1 Will stocks still lose money in bull market?
I bought stocks with my own money and it was changing every day. Who wouldn’t care, especially at the beginning, I hope it could rise and rise. Although it is unrealistic, there are still people who make money. However, once you open the stock software, there are more than a thousand stocks, red, green and green, index market, and individual stock K-line indicators. You will talk about how fundamentals are important, and you will talk about what you can make a lot of money with a subject. You will be dizzy and confused.
Funds are your helper. It concentrates the dispersed funds of investors and leaves them to professional managers for management, investing in stocks, bonds, foreign exchange and other fields. The income from fund investment belongs to the holders, and the management agency charges a certain proportion of custody and management fees. Think about it, what are the benefits of doing this?
Since many investors have too many connections to many stocks, it is not conducive to operation, this is called too high coupling in software. After having funds, many users only deal with funds and care about the rise and fall of funds. In fact, the operation is that fund managers deal with thousands of stocks and other investment products.
Here we actually mention a design pattern that is used a lot in object-oriented development - appearance pattern and storefront pattern. First try to write down the code of stock investors' stock trading.
12.2 Stock trading code
Customer category, stock category 1 (action buying and selling), stock category 2, bond category, etc. Users need to understand the situation of stocks and bonds, need to participate in the specific buying and selling of these projects. Very high coupling.
12.3 Investment Fund Code Add Fund Class
Clients, fund categories (it needs to understand the methods and attributes of all stocks or other investment methods and combine them for external calls. Industry - fund purchase and fund redemption), stock category 1 (action buying and selling), stock category 2, bond category, etc.
The client calls, at this time, the user does not need to know the stock, and can even know nothing about the stock. After buying the fund, he goes home to sleep. After a period of time, he can count the money. The specific transactions of participating in stocks are completed by the fund company. The client code is simple and clear.
This way of writing is actually the basic code structure of the appearance pattern.
12.4 Appearance Mode (Facade)
Appearance mode: Provides a consistent interface for a set of interfaces in the subsystem. This mode defines a high-level interface, which makes this subsystem easier to use.
For friends who have a certain foundation in object-oriented, even if they have never heard of the appearance pattern, it is entirely possible to use it in many cases, becauseIt perfectly embodies the idea of the principle of reliance and the principle of Dimit, so it is one of the most commonly used patterns.。
12.5 When to use Appearance Mode
This is divided into three stages:
first,In the early stages of design, you should consciously separate the two different layers. For example, a classic three-layer architecture requires consideration of establishing an appearance between the data access layer and the business logic layer, the business logic layer and the presentation layer.This can provide a simple interface for complex subsystems, allowing for greatly reduced coupling.
Secondly,In the development stage, Subsystems often become more and more complex due to continuous reconstruction and evolution. Most patterns will produce many small classes when used, which is a good thing, but it also brings difficulties to user programs that call them externally. Adding appearance Facade can provide a simple interface and reduce the dependence between them.
third,While maintaining a legacy large system, This system may be very difficult to maintain and expand, but because it contains very important functions, new requirements development must rely on it. It is also very suitable to use Facade in the appearance mode at this time.
In summary, you can develop a Facade class for the new system to provide a clear and simple interface for rough or highly complex legacy code design, allowing the new system to interact with Facade objects and Facade to interact with legacy code for all the complex work.
For complex and difficult-to-maintain old systems, directly modifying or expanding them may cause many problems. They are divided into two groups. One is to develop the interaction between Facade and the old system, and the other is to understand the Facade interface and directly develop new system calls these interfaces, which can indeed reduce a lot of unnecessary trouble.
Chapter 13: Every good dish is different - Builder mode
13.1 No salt added to the fried noodles
No matter which store you eat at McDonald's and KFC's burgers, at least in China, the taste is basically the same. And our country, such as the fish-flavored shredded pork, is a dish that almost all Chinese restaurants of all sizes, but it can taste tens of thousands of flavors. Why is this? Because the chefs are different, everyone's practices are different.
McDonald's and KFC are relatively standardized. The taste is determined by their engineering process. There are strict regulations on how much raw materials are placed and how many minutes to heat them.
Whether our fried noodles are filled with salt or not, whether they are delicious or not, is determined by the cook. A good mood is a plate of good noodles, and a bad mood is a plate of garbage.
I ate two dishes of garbage today.In fact, the most important thing here is that whether we eat well or uncomfortable, we have to rely on the chef.. Think about the principles of our design patterns?
Reliance inversion principle? Abstraction should not rely on details, details should rely on abstraction. Since the dishes we want to eat depend on the details of the chef, we are very passive.
Lao Mai Lao Ken’s workflow can be an abstract process, and details such as specific grills and how long to bake depend on this abstraction.
13.2 Build a villain
It is very common to ask a villain to use a program to draw a villain. Now it is simpler. It requires that a villain must have a head, body, hands and feet.
Implementation: Create a yellow brush and draw the head, body, left hand, right hand, left foot, right foot on the canvas.
Now I'm asking to draw another fat villain
Implementation: Just change one of the brushes. But I didn't draw one leg.
This is just like eating fried noodles. The boss forgot to add salt, which made the very delicious late-night snack boring. In game programs, when drawing people, head, body, hands and feet are indispensable. No matter what character it is, it is indispensable during development.
The current code is written in a separate form. What if you need to use these programs to draw villains elsewhere?
13.3 Building Little Man Two
Separate and build two categories, one is thin human and the other is fat human. No matter who is, you can call it.
Thin human: determine the artboard and color during initialization, build() to build the villain
Fat human: determine the drawing board and color during initialization, build() to build the villain
Client implementation: Create thin humans and fat humans
Writing this way still does not solve the problem of forgetting to add salt to the fried noodles. For example, if you need to add a tall villain now, will he be missing his arms and legs because he is not careful about programming?
The best way is to stipulate that all villains must have a head and body, as well as hands and feet.
13.4 Builder Mode
Builder: Separate the construction of a complex object from its representation, so that the same construction process can create different representations.
A careful analysis will reveal that the "process" of building villains here is stable and requires head, body, and skills, while the "details" of the specific construction are different, including fat, thin, tall and short. But for users, I don’t care about this. I just want to tell you that I need a fat guy to play, so you just build one for me.. If you need to separate the construction of a complex object from its representation so that the same construction process can create different representations intents, we need to apply it to a design pattern, the Builder pattern, also known as the generator pattern.
The builder model can separate the internal appearance of a product from the product generation process, so that a construction process can generate product objects with different internal appearances. If we use the construction mode, then the user only needs to specify the type to be built to get them, and the specific construction process and details do not need to be known.
So how to use the Builder model?
First of all, what do we need to draw villains? Head, body, left hand, right hand, left foot, right foot.
Right, so we first define an abstract human-building class to stabilize this process and not let anyone forget any step.
Then, we need to build a thin villain and let the thin subclass inherit this abstract class, and we must rewrite these abstract methods. Otherwise, the compiler won't let you pass.
In this way, when the client wants to call, you still need to know the methods of head, body, skills, and skills, which has not solved the problem.
We also lack a very important class in the construction mode, the Director, which uses it to control the construction process and also uses it to isolate the user from the construction process.
The user tells the commander PersonDirector what kind of villain I need and build the villain createPerson according to the user's choice.
The purpose of the PersonDirector class is to build villains step by step according to the user's choice. When the construction process is completed by the commander at this time, the user does not need to know. Moreover, since every step of this process must be done, the problem of drawing one hand or one leg will not occur.
13.5 Builder mode analysis
Builder pattern structure diagram
Builder is an abstract interface specified for each component of a Product object.
Director commander is to build an object that uses the Builder interface.
ConcreteBuilder, the specific builder, implements the Builder interface, constructs and assembles various components.
Product, specific product.
What is Builder? It is an abstract class that builds various parts of the villain. In summary, it is an abstract interface specified for the creation of each component of a Product object.
What is ConcreteBuilder? For specific villains, how to draw various parts of the villain's head, body, hands and feet. It is a specific builder that implements the Builder interface, constructs and assembles various components.
What is Product? The role of the product.
What is Director? Commander is used to build villain objects based on user needs. It is to build an object using the Builder interface.
When do you need to use Builder mode?
It is mainly used to create some complex objects. The construction order between the internal constructions of these objects is usually stable, but the internal construction of objects usually faces complex changes.
The advantage of the builder is that the construction code is separated from the representation code. Since the builder hides how the product is assembled, if you need to change the internal representation of a product, you only need to define a specific builder.
13.6 Builder Mode Basic Code
Product class - Product class, composed of multiple components, behavior add(string part) - Add product parts
Builder class - Abstract Builder class, determines that the product consists of two components BuilderPartA and BuilderPartB, and declares a method to obtain the results after the product is built GetResult.
ConcreteBuilder1 category - specific builder category, the two specific components to be built are component A and component B.
Director class - command class, used to direct the construction process.
Client code, customers do not need to know the specific construction process. The commander uses the ConcreteBuilder1 method to build the product.
So, the builder pattern is the pattern that applies when the algorithm for creating complex objects should be independent of the components of that object and how they are assembled.
If the boss who made fried noodles today knew the builder model, he would understand that salt must be placed, otherwise the compilation would not be possible.
Chapter 14 The boss is back, I don’t know——Observer mode
14.1 The boss is back? I have no idea!
Through the front desk secretary, if the boss comes back after going out, he will call in, and everyone (colleagues) will take their place immediately, so that the boss will not find the problem.
14.2 Bidirectional coupling code
The situation mentioned above is actually a typical observer model.
Front desk secretary class: class Secretary (notifier class)
Own properties: colleague list<StockObserver>
Method of possession: Add (that is, a few colleagues ask the front desk for help, so they add several objects to the collection) void Attach(StockObserver observer)
How to own: Notify (when the boss comes, I will send a notice to all registered colleagues, and the boss is here) void Notify()
Owning method: Front desk status (the front desk passes the phone, what is said or what is done) string SecretaryAction
Look at the stock colleague class: class StockObserver (observer class)
Method of owning: update (get notification from the front desk and take action quickly) void update()
Client:
Initialize the front desk secretary class; initialize the creation of the colleagues who look at stocks (two); record the two colleagues in the front desk.Attach(observer); find that the boss is back.SecretaryAction = "The boss is here"; notify the two colleagues.Notify();
In the above code, the two classes are coupled to each other, and the front-end class needs to add observers, and the observer class needs the front-end state. If there are still people among the observers who want to watch the NBA live broadcast online, the front desk category needs to be changed.
According to the design principle, first of all, the open-closed principle, modifying the original code means that the design is not good enough. The second is the principle of dependency reversal, and programs should be made to rely on abstraction rather than interdependence.
14.3 Decoupling Practice 1
Add abstract observer abstract class Observer abstract method update void update();
Add two specific observers to inherit abstract observers class StockObserver (colleague who looks at stocks): Observer, rewrite the update method.
Add two specific observers to inherit abstract observers class NBAObserver (see colleagues in NBA): Observer, rewrite the update method.
Front desk secretary class: class Secretary, changing all the places coupled with specific observers into abstract observers. For abstract programming, reduce coupling with concrete classes
Front desk secretary is also a concrete category and should also be abstracted. The notifier may be the front desk secretary or the boss.
14.4 Decoupling Practice II
Add abstract notifier interface
Add (that is, a few colleagues asked the front desk for help, so they added several objects to the collection) void Attach(Observer observer)
Reduce (if a colleague has conflicts with the front desk (notifying the notifier), so he will not notify the colleague) void Deatch (Observer observer)
Issuing notification void Notify();
State (the front desk is by phone, what is said or what is done)String subjectState{get;set;}
The specific notification class may be the front desk or the boss. They may have their own methods, but for the notifier, they are the same, so they all implement this interface.
Boss class Boss:Subject Both list, increase, decrease, notification, boss status
For specific observers, the point that needs to be changed is to change the areas coupled with the "front desk" to target abstract notifiers.
14.5 Observer Mode
Observer mode is also called Publish/Subscribe mode.
Observer pattern: Defines a one-to-many dependency, allowing multiple observer objects to listen to a topic object at the same time. When this subject object changes state, it notifies all observer objects so that they can automatically update themselves.
Observer structure diagram
The Subject (Abstract Notifier) class, which saves all references to the observer object in a gathering, each topic can have any number of observers, and the abstract topic provides an interface to add and delete observer objects.
The Observer class, an abstract object, defines an interface for all concrete observers and updates itself when it is notified of the topic.
The ConcreteSubject class, the notifier of the specific topic, stores the relevant state into the specific observer object; when the internal state of the specific topic changes, a notification is issued to all registered observers.
The ConcreteObserver class, a concrete observer, implements the update interface required by the abstract observer role, so that its own state is coordinated with the state of the topic.
14.6 Observer mode features
What is the motivation for using the observer?
There is a very bad side effect of dividing a system into a series of collaborative classes, which is the need to maintain consistency between related objects.We do not want to tightly couple all kinds of things to maintain consistency, which will make it difficult to maintain, expand and reuse inconvenient. The key object of the Observer pattern is the theme(Notified)Subjectand observersObserver,oneSubjectThere can be any number of dependent on itObserver,onceSubjectThe state of change has occurred,AllObserverAll can be notified。 When a Subject sends a notification, it does not need to know who its observer is, that is, who the specific observer is, it does not need to know at all. And no specific observation is known nor does it need to know the existence of other observers.
When should I consider using observer mode?
When the change of one object requires changing other objects at the same time. And it does not know how many objects are to be changed, and it should consider using the observer pattern.. When an abstract model has two aspects, one of which depends on the other, the observer pattern can be used to encapsulate the two in separate objects so that they each independently change and multiplex.
In general, the work done by the observer model is actually to decouple. Let both parties of the coupling rely on abstraction rather than concrete. Therefore, the changes used by each will not affect the changes on the other side (this is also the best manifestation of dependency inversion).
When abstract observers, the code uses abstract classes, why not use interfaces?
Because the two concrete observers, looking at stock observers and looking at NBA observers, are similar, so abstract classes are used, so some code can be shared.
So can abstract observers be defined using interfaces? interface observer{void update();}
In real-life programming, specific observers may be completely unrelated to classes, but they all need to make Update() operations based on the notification of the notifier, so letting them all implement one interface to realize this idea.
14.7 Deficiencies in Observer Mode
Go back to the "boss, front desk and colleagues' example" just now and see what else is there?
Although the principle of dependency reversal has been used, the "abstract notifier" still relies on the "abstract observer", that is, if there is no interface like the abstract observer, the function of my notification will not be completed. In addition, for each specific observer, it does not necessarily mean that the "update" method needs to be called. As I said just now, what I want is that the "toolbox" is hidden and the "automatic window" is open, which is not a method with the same name at all. This is where the shortcoming is.
It would be great if the notifier and the observer don't know each other at all and the client decides who to notify.
14.8 Event delegation implementation
The "Look at Stock Observer" class and the "Look at NBA Observer" class have removed the parent class "Abstract Observer" class, so some code is added and the "Update" method name is changed to the appropriate method name for each.
Colleagues looking at stocks: StockObserver-void CloseStockMarket() method is changed to "Close Stock Program".
Colleagues watching NBA: NBAObserver-void CloseNBADirectSeeding() method "update" name to "Close NBA Live Broadcast".
This is what it is in reality, and the method names are not necessarily the same.
Since the abstract notifier does not want to rely on the abstract observer, the method of increasing or decreasing is unnecessary (the abstract observer no longer exists).
Notifier interface: interface Subject{void Notify();}
The following is how to deal with the boss class and front desk class. The notification method in them has traversed the observer, so it cannot be underestimated. But ifIn .NET, We can use a very good technology to deal with this problem, which is called delegate.
Declare a delegate called EventHandler (event handler), no parameters, no return value. delegate void EventHandler();Delegate is used to implement indirect calls of function methods
Boss class and front-end secretary class: declare an event Update, type delegate EventHandler; public event EventHandler Update; (declare an EventHandler (event handler), named Update (update))
When accessing the notification method, call update public void Notify(){Update();}
Client: Link the method of closing stocks by viewing stockists and the method of closing NBA live broadcast by viewing NBAs to the boss's update, that is, entrusting two different methods of different types to the boss's update. += new EventHandler();
14.9 Event Entrustment Note
Delegation is a reference method type. Once a method is assigned to the delegate, the delegate will have the exact same behavior as the method. The use of a delegate method can have parameters and return values like any other method. Delegation can be regarded as an abstraction of a function, a class of a function, and the instance of the delegate will represent a concrete function.
delegate void EventHandler(); can be understood as declaring a special class, while public event EventHandler Update; can be understood as declaring a class variable (it should be declared that an event delegate variable is called update).
The delegated instance will represent a specific function, which means that new EventHandler() is actually an instance of delegated, and it is equivalent to delegating this method to this method.
Once a method is assigned to the delegate, the delegate will have the exact same behavior as the method. Moreover, a commission must carry multiple methods, and all methods are ringed in turn. More importantly, it can make the methods carried by the delegate object not need to belong to the same class.
This makes it unnecessary to add or decrease the collection of abstract observers that were originally added or decreased in the boss class and to traverse during notification. Go to the client to get the delegation to carry multiple methods, which solves the original coupling problem with abstract observers.
But there is also a prerequisite for delegation, that is, all methods carried by delegation must have the same prototype and form, that is, they must have the same parameter list and return value type.
Note that there is first the observer model and then the commissioned event technology.
14.10 Ishi Shouji's commission after losing his mobile phone
Shi Shouji's cell phone was lost and his original classmate's number was gone. He entrusted Xiaocai to help copy a copy of his classmate's number and email him. However, there are many people in the class, and it is easy to make mistakes when copying. Moreover, if students have urgent matters to look for, they still cannot find them.
Using the observer mode, Xiaocai sends a short message to all the students in the class here to inform them that Shi Shouji has changed his number and please update the number.
Chapter 15 Can’t you not change DB? ——Abstract factory pattern
15.1 Can't you not change DB?
To build an e-commerce website for a company, SQL Server is used as a database. Then, the company received a project with similar needs from another company, but the company could only use Access, so it had to transform the original project code.
15.2 The most basic data access program
The factory method pattern is to define an interface for creating objects, allowing the subclass to decide which class to instantiate.
15.3 Data access program using factory method mode
Code structure diagram:
IUser interface, used for client access, uncoupling from specific database access. interface IUser{void Insert(User user);User GetUser(int id)}
SqlserverUser class, used to access the User of SQL Server. class SqlserverUser:IUser
Accessuser class, used to access Access User. class AccessUser:IUser
IFactory interface, defines an abstract factory interface that creates access to User table objects. interface IFactory{IUser CreateUser();}
SqlServerFactory class, implements the IFactory interface, and instantiates SqlserverUser. class SqlServerFactory:IFactory{IUser CreateUser(){return new SqlserverUser();}}
AccessFactor class, implements the IFactory interface, and instantiates AccessUser. class AccessFactory:IFactory{IUser CreateUser(){return new AccessUser();}}
Client code: IFactory factory = new SqlServerFactory(); To change it to Access database, you only need to change the real name to IFactory factory = new AccessFactory();
However, the code still specifies new SqlServerFactory(), and there are still many things to be changed. It is impossible to have only one User table in the database, but there are likely other tables, such as adding department tables (Department tables). What should I do at this time?
15.4 Data access program using abstract factory mode
The steps to add a Department table are the same as the User table. Just change IFactory factory = new AccessFactory() to IFactory factory = new SqlServerFactory() to realize the switching of database access.
In fact, without realizing it, a very important design model has been reconstructed through the continuous evolution of needs.
When there is only one User class and User operation class, only the factory method mode is needed, but now there are obviously many tables in the database, and SQL Server and Access are two different categories. Therefore, to solve this problem involving multiple product systems, there is a special factory mode called abstract factory mode.
15.5 Abstract factory pattern
Abstract Factory: Provides an interface to create a series of related or interdependent objects without specifying their specific classes.
Abstract factory structure diagram:
AbstractFactory: Abstract factory interface, which should contain abstract methods for all product creation CreateProductA();CreateProductB();
ConcreteFactory1: ConcreteFactory2: A specific factory, creating product objects with specific implementations.
AbstractProductA: AbstractProductB: AbstractProductB: AbstractProduct, they all have two different implementations.
ProductA1: ProductA2: ProductB1: ProductB2: Implementation of the specific classification of two abstract products.
AbstractProductA and AbstractProductB are two abstract products. The reason why they are abstract is that they may have two different implementations. As for the example just now, it is User and Department, while ProductA1, ProductA2, ProductB1, and ProductB2 are implementations of the specific classification of two abstract products. For example, ProductA1 can be understood as SqlserverUser, and ProductB1 is AccessUser.
In this way, IFactory is an abstract factory interface, which should contain all abstract methods for product creation, and ConcreteFactory1 and ConcreteFactory2 are specific factories. Just like SqlserverFactory and AccessFactory.
Usually, an instance of the ConcreteFactory class is created at the run time, and this specific factory creates a product object with a specific implementation. That is to say, in order to create different product objects, the client should use different specific factories.
15.6 Advantages and disadvantages of abstract factories
What are the benefits of doing this?
The biggest advantage is that it is easy to fight the product system. Because specific factory classes, such as IFactory factory = new AccessFactory(), only appear once in an initialization, which makes it very easy to change the specific factory of an application. It only needs to change the specific factory to use different product configurations.
Our design cannot prevent changes that are needed, so our ideal is to minimize changes. Now if you want to change database access, we only need to change the specific factory to do it. The second biggest advantage is that it separates the specific instance creation process from the client. The client manipulates the instance through their abstract interfaces, and the specific class names of the product are also separated by the implementation of the specific factory and will not appear in the client code. In fact, in the example I just wrote, the client only knows IUser and IDepartment. I don’t know whether it is implemented using SQL Server or Access.
Abstract mode also has its disadvantages. If a mode is, there will be disadvantages and there will be times when it is not applicable. Abstract factory mode can easily switch code accessed by two databases. But if you need to add functions, such as adding project table Project now, what are the changes needed?
Then you need to add at least three classes, IProject, SqlserverProject, AccessProject, and you also need to change IFactory, SqlserverFactory and AccessFactory to fully implement it. This is too bad.
Also, obviously there will be no one client program class. There are many places that use IUser or IDepartment. In fact, at the beginning of each class, IFactory factory = new SqlServerFactory needs to be declared. If I have 100 classes that call database access, do I have to change code like IFactory factory = new SqlServerFactory 100 times? This does not solve the requirement that I change the database access completely when I change it.
Programming is an art, and such large-scale changes are obviously very ugly.
15.7 Using simple factories to improve abstract factories
Remove the three factory classes IFactory, SqlserverFactory and AccessFactory, and replace it with the DataAccess class, which is implemented in a simple factory mode.
Since the db value (Sqlserver and Access) is set in advance, the simple factory method does not require input parameters. In this way, the client only needs to generate a specific database access class instance with (). The client does not have any SQL Server or Access words to achieve the purpose of decoupling.
However, there are still some problems with this method. The reason is that if you need to increase Oracle database access, the abstract factory only needs to add an OracleFactory factory class, but now it is more troublesome. You need to add case to the switch of each method in the DataAccess class。
15.8 Data access program using reflection + abstract factory
What we need to consider is whether it can be written in the program. If it is Sqlserver, instantiate the SQL Server database-related classes, and if it is Access, instantiate the Access-related classes and other statements. Instead, search for the class that should be instantiated somewhere based on the value of the string db. This way our switch can say goodbye to it.
The so-called reflection refers to a mechanism for obtaining attributes and methods in a class and calling methods in it in the runtime state. The function of this mechanism is to obtain the class (Class) known only at runtime, its properties (Field), methods (Method) and calls the methods, and also set the property values in it.
Reflection format: .NET implementation method = ("assembly name").CreateInstance("namespace.class name"), java implementation method = ("package name.class name");
As long as you write using SystemReflection on the top of the program to reference Reflection, you can use anti-thank you to help us overcome the inherent shortcomings of the abstract factory model.
The instantiation effect is the same, but what is the difference between these two methods?
The conventional method is to state that you want to instantiate the SqlserverUser object. The writing method of anti-thank you actually also indicates that the SqlserverUser object should be instantiated. The conventional method cannot be flexible and more AccessUser. In reflection, CreateInstance ("Abstract Factory Mode.SqlserverUser") can be flexible and more searched for SqlserverUser as AccessUser.
The reason is that this is a string, which can be processed with variables, and can be replaced as needed.
The main difference here is that the original instantiation was written in the program, but now using reflection, you can use strings to instantiate objects, while variables can be replaced.
It's so harsh to write in the program. To be precise, it is to convert the program from compile time to runtime. Since the strings in CreateInstance ("Abstract Factory Mode.SqlserverUser") can be written as variables, and whether the value of the variable is SQL Server or Access can be determined by the previous db variable. So the trouble of switching judgment is eliminated.
Overall, it feels a bit flawed, because when changing the database access, you still need to change the program (change the value of the db string) and recompile it. If you can not change the program, it is truly in line with the open-closing principle.
15.9 Use reflection + configuration files to implement data access program
You can use configuration files to solve the problem of changing DataAccess. Add a file, assign values to the DB string, and write whether it is Sqlserver or Access in the configuration file, so that the DataAccess class does not need to be changed.
Now we apply the reflection + abstract factory pattern to solve the problem of maintainable and scalable database access.
From this perspective, all places where you use a simple factory, you can consider using reflection technology to remove switch or if and uncouple the coupling caused by branch judgment.
15.10 No obsession, no success
Chapter 16 When will I rest in endless overtime work? - Status mode
16.1 Overtime, overtime again
I was in good condition in the morning, wanted to sleep at noon, but gradually recovered in the afternoon, and I was suffering from overtime. In fact, it is a change in states, and different times will have different states. Implementing with code is actually to make judgments based on the time.
16.2 Working Status-Function Edition
Define a "write program" function to reflect different working states according to different time: WriteProgram(){if(Hour<12){}else if(Hour<13){}}
16.3 Working Status-Classification Edition
Define work class, provide void WriteProgram() method, and judge different time states
16.4 Too long methods are bad taste
The method is very long and there are many judgment branches, which means it has too much responsibility. No matter what state it is, it needs to be changed through it, and any changes or additions to require changes to this method, which is actually very bad. Object-oriented design is actually to achieve responsibility decomposition of code.
Think of ways to turn these branches into classes after classes, and the addition will not affect other classes. The state changes are then completed in their respective classes. againstThis type of problem Gof provides a solution, that is, state mode.
16.5 Status Mode
State mode: When an object's internal state changes, it allows the change of period behavior, and this object looks like you have changed its class.
The state mode mainly solves the situation when the conditional expression that controls the state transition of an object is too complex. Transferring the state judgment logic to a series of classes representing different states can simplify the complex judgment logic. Of course, if the state judgment is simple, there is no need to use the state mode.
Status structure diagram:
abstract class State{abstract void Handle(Context context);} State class, abstract state class, defines an interface to encapsulate behaviors related to a specific state of the Context.
class ConcreteStateA:state{override void Handle(Context context){ = new ConcreteStateB();//Set the next state of ConcreteStateA is ConcreteStateB}} ConcreteState class, specific state, each subclass implements a behavior related to a state of Context.
class ConcreteStateB:state{override void Handle(Context context){ = new ConcreteStateA();//Set the next state of ConcreteStateB is ConcreteStateA}}
class Context{State state;void Request(){(this);//process the request and set the next state}}Context class to maintain an instance of the ConcreteState subclass, which defines the current state.
Client Main{Context c = new Context(new ConcreateStateA();//Set the initial state of Context to ConcreteStateA();();//Continuous requests and changes state at the same time)}
16.6 Benefits and uses of status mode
The advantage of state mode is that it localizes behaviors related to a specific state and divides behaviors from different states.
In fact, it is to put all the behaviors related to a specific state into an object. Since all state-related codes exist in a ConcreteState, it is easy to add new states and transformations by defining new subclasses.
The purpose of this is to eliminate the huge conditional branch statements. The branch judgments make them difficult to modify and expand. Just like the engraving printing we first mentioned, any changes or changes are fatal.
State mode reduces mutual dependence by dividing various state transition logics into subclasses of State, just like changing the entire layout into one movable type after another, which is easy to maintain and expand at this time.
When should I consider using state mode?
When an object's behavior depends on its state, and it must change its behavior according to its state at run time, you can consider using state mode.In addition, if a business requires that a certain business has multiple states, it is usually enumerated variables, and the state changes are achieved by a large number of branch judgment statements. At this time, you should consider defining each business state as a subclass of a State.In this way, these objects can change independently without relying on other objects. One day, customers need to change their needs, increase or decrease their business status or change their status process, which is not difficult for us.
16.7 Working status-state mode version
Code structure diagram:
Abstract state class, define an abstract method "write program" abstract class State{abstract void WriteProgram(Work w)}
Morning and noon working status class: class ForenoonState:State{override void WriteProgram(Work w){if( < 12){}else{(new NoonState());();//After 12 o'clock, you will be transferred to the noon working status}}}
Noon Working Status Class: class NoonState:State{override void WriteProgram(Work w){if( < 13){}else{(new AfternoonState());(); After 13 o'clock, the afternoon working status will be transferred to the afternoon working status}
Work status category in the afternoon and evening:
Evening working status category:
Sleep status and rest status after get off work:
Work class, there are no too long branch judgment statements at this time: class Work{Work(){sate = new ForenoonState();//The work is initialized to the morning working state, that is, to start work at 9 am}}
At this time, if we want to complete the code "employees must leave the company before 20 o'clock", we only need to add a "forced leave of get off work status" and change the judgment of the working status class for an evening, which is a code that does not affect other states.
Chapter 17 I need to translate in NBA - Adapter Mode
17.1 I need a translation in the NBA
17.2 Adapter Mode
Adapter mode: converts an interface of one class into another interface that the customer wants. Adapter mode enables classes that cannot work together because of incompatibility of interfaces.
What problems does the adapter mode mainly solve?
Simply put, what is needed is right in front of you, but it cannot be used, and it cannot be modified in a short period of time, so we find a way to adapt it.
What does adaptation mean?
This word first appeared in electrical engineering. Some countries use 110V voltage, while our country uses 220V. However, our electrical appliances, such as laptops, cannot be used for any voltage, but different countries, and the voltage may be different. So we use a power adapter. As long as it is electricity, no matter how many volts, it can turn the power supply into the required voltage. This is the function of the power adapter.The meaning of an adapter is something that fits one thing into another.
Yao Ming just went to the NBA to play and didn’t have time to learn English seriously in school before. It is difficult to learn immediately to understand and speak.
Under the previous question of not changing the team's coaches, players and Yao Ming, all we can do is find a way to find an adapter.
In software development, that is, when the data and behavior of the system are correct but the interface does not match, we should consider using an adapter, with the purpose of using an original object outside the control range to match an interface.The adapter mode is mainly used in situations where some existing classes are desired, but the penetration is inconsistent with the requirements of the reuse environment. For example, it is of practical value in applications such as the need to reuse some functions for early code.
Adapter structure diagram:
class Target{virtual void Request(){normal request}}; Target This is the interface that customers expect. The target can be a concrete or abstract class or an interface.
class Adaptee{void SpecificRequest(){Special Request};}; The class that Adaptee needs to adapt
class Adapter:Target{new Adaptee()//Create a private Adaptee object; override void Request(){();//This way you can turn the surface call to the Request() method into the actual call to SpecificRequest()};}
class Main{Target target = new Adater();();//For the client, the call is Target's Request()}; client class
17.3 When to use Adapter Mode
When you want to use an existing class, but if its interface, that is, its methods are different from your requirements, you should consider using the adapter mode.
The things that both classes do are the same or similar, but have different interfaces to use. Moreover, since classes share the same interface, the same interface can be called uniformly using client code, which can be simpler, more direct and more compact.
In fact, using the adapter mode is also a helpless move. It feels like "repairing the tight" is a bit like "repairing the tight". There is no way, the software will be maintained for one day, and the maintenance may cause similar functions and different interfaces due to different developers, different products, and different manufacturers. This is the time for the adapter mode to show off.
Is there any time when you need to consider using adapter mode at the beginning?
Of course, there are, for example, when a company designs a system, it considers using a third-party development component, and the interface of this component is different from our own system interface, and there is no need for us to modify our own interface to cater to it. At this time, although it is in the development design stage, you can also consider using the adapter mode to solve different interface problems.
17.4 Basketball Translation Adapter
17.5 Adapter mode .NET application
There is a very important adapter that the class library has implemented in .NET, that is DataAdaper. DataAdapter is used as an adapter between DataSet and data source to retrieve and save data. DataAdapter provides this adapter by mapping Fill (which changes the data in the DataSet to match the data in the data source) and Update (which changes the data in the data source to match the data in the DataSet). Since the data source may come from SQL Server, from Oracle, or from Access and DB2, these data may be different in organization, but we want to get a unified DataSet (essentially XML data). Using DataAdapter is a very good method at this time. We do not have to pay attention to the data details of different databases to use data flexibly.
17.6 Bian Que’s medical skills (see the book for details)
If different interface problems can be prevented in advance, mismatch problems will not occur;When there is a small problem of inconsistent interfaces, refactor it in time and the problem will not be expanded;Only when you encounter situations where the original design and code cannot be changed, you will consider adaptation.。
Post-control is not as good as in-process control, and during-process control is not as good as in-process control. Adapter mode is of course a good mode, but if you ignore its application and use it blindly, it is actually putting the cart before the horse.
Chapter 18 If you go back to the past - Memo Mode
18.1 If I give me another chance...
In stand-alone PC games, you usually save a progress before playing big bosses. Then, if you fail to pass the level, you can return to the previous progress to restore the original state and start over.
Usually this kind of saving is stored on the disk for future reading. However, for some more conventional applications, such as if we need to regret chess when playing chess, undo it when writing documents, and back back when viewing web pages, these relatively frequent and simple recovery do not need to be stored on disk, just restore the state saved in memory. This is a more common application and is used in many developments.
Implement the scenario and implement it with code. A game character has data such as vitality, attack power, defense power, etc., which will definitely be different before and after fighting the boss. We allow players to restore the game to the front of the duel if the effect of the duel with the boss is not ideal.
18.2 Game saving progress
class GameRole{int vit;//Lifety int atk;//Attack power int def;//Defense void StateDisplay(){//Status display};void GetInitState(){vit=atk=def=100//Get initial state};void Fight(){vit=atk=def=0//Fight}}; Game character class, used to store data of the character's vitality, attack power, and defense.
class Main{front = new GameRole();//Get the initial character status before the battle against the boss. backup = new GameRole();backup = front;//Save the progress, save the progress through a new instance of the game character (); During the battle against the boss, the loss is serious. All data is completely lost. Front = backup; Unwilling to give up at the end of the game, restore the previous progress, and play again};
There are many problems with the above code, mainly lies in the call of this client. Because writing this way will expose the details of the entire game character to the client, your client's responsibilities are too great. You need to know the details of the game character's vitality, attack power, and defense, and also backup it. In the future, new data needs to be added, such as increasing magic power or modifying a certain current power, such as changing vitality to experience value, and this part must be modified. The same principle exists in the code at recovery.
Obviously, we want to encapsulate the access state details of these game characters, and preferably in external classes. To reflect the separation of responsibilities.
18.3 Memorandum Mode
Memento: Capture the internal state of an object without destroying encapsulation and save this state outside the object. In this way, the object can be restored to its original saved state later.
Memorandum pattern structure diagram:
Originator (initiator. Corresponding to game character class): Responsible for creating a memorandum Memento to record its internal state at the current moment, and can use the memorandum to restore the internal state. Originator can determine which internal states of the Originator are stored by Memento as needed.
Memento: Responsible for storing the internal state of the Originator object and preventing other objects other than Originator from accessing the Memento. Memos have two interfaces. Caretaker can only see the narrow interface of the memo, and it can only pass the memos to other objects. Originagor is able to see a wide interface that allows it to access all the data needed to return to the previous state.
Caretakeer (administrator): Responsible for saving the memorandum Memento, and cannot operate or check the content of the memorandum.
As for the example just now, the game character class is actually an Originator, and the same game character instance backup is used to make memos. This can be considered when saving all information. Using clone to achieve the state saving of Memento may be a better way. However, if this is the case, it is equivalent to opening the entire (public) interface of Originator to the upper layer application, which is sometimes inappropriate for saving backups.
If we do not need to save all the information for use, this may be more likely to happen. What we need to save is not all the information, but only part of it. Then there should be an independent memorandum class Memento, which only has the attributes of the information that needs to be saved.
18.4 Basic code of memo mode
Initiate human: class Originator{string state;//The attributes that need to be saved may have multiple Memento CreateMemento(){return (new Memento(state));//Create a memorandum, import the information that needs to be saved and instantiate a Memento object}; SetMeenento(Memento memento){state = ;//Restore the memorandum, import Memento and restore the relevant data}}.
Memorandum class: class Memento{string state;Memento(string state){//Construction method, import relevant data into}; string State{get{return state;//The data attributes that need to be saved can be multiple};}}.
Manager class: class Createker{Memento memento; Memento Memento{get{return memento;}set{memento = value;}//Get or set memorandum}}.
Client class: class Main{Originator o = new Originator();="on";//Originator's initial state, the state property is On Caretakeer c = new Caretakeer(); = ();//When saving the state, due to good encapsulation, the implementation details of the Originator may be hidden = "off";//Originator changed the state property to off ();//Restore the original state}
This is to encapsulate the details to be saved in Memento. It doesn’t need to affect the client when changing the details to be saved.
In what occasions do the memo mode be used?
Memento mode is more suitable for classes with relatively complex functions but need to maintain or record attribute history, or when the attributes that need to be saved are only a small part of many attributes, the Originator can restore to the previous state based on the saved Memento information.
The command mode also achieves a similar undo,If the command mode needs to be implemented when using command mode in a certain system, the command mode can use the memo mode to store the state of the revocable operation.Sometimes the internal information of some objects must be stored outside the object, but must be read by the object itself. At this time,Use memos to block complex object internal information from other objects, so that the encapsulation boundaries can be properly maintained.
Its greatest functionOr when the character's state changes, it is possible that this state is invalid. At this time, you can use the temporarily stored memo to restore the state.
18.5 Game progress memo
The memorandum mode also has its disadvantages. The role status needs to be completely stored in the memorandum object. If the state data is very large, then the memorandum object will consume a lot of memory in terms of resource consumption. So it is not that the more you use, the better.
Chapter 19 Branch =-Department - Combination Model
19.1 Is the branch a department?
There is a company with headquarters in Beijing and high branches in several major cities across the country, such as Shanghai, and also has offices in some provincial capital cities, such as Nanjing Office and Hangzhou Office. Now there is a question: the human resources department, accounting department and other office management functions of the head office need to be available in all branches or offices. What should I do?
I have said before that simple replication is the worst design, so the idea is to share functions to each branch, that is, let the headquarters, branch, and office use the same set of codes, just distinguish them according to the ID.
This method cannot be distinguished because of their requirements that the headquarters, branches and offices are in a tree-like structure, that is, organized structure, and cannot be managed in a simple parallel manner, because in actual development, one by one, it is necessary to judge whether it is the headquarters or the finance of the branch one by one, and then implement its corresponding method.
This situation is common. For example, merchants selling computers can sell individual accessories or assemble the whole machine. For example, copying files can be copied and pasted one by one or the entire folder for copying. For example, text editing can bold, change color, and change the font of a single word. Of course, the same operation can be done for the entire paragraph of text. Its essence is the same problem.
In fact, the relationship between the branch or office and the head office is the relationship between the part and the whole.
I hope that the organizational structure of the head office, such as the management functions of the Human Resources Department and the Finance Department, can be reused for the branch. This is actually a question where the whole and the part can be treated consistently.
Let’s analyze the project I just mentioned. If the Beijing Corporation is regarded as the root of a big tree, its subordinate branches are actually the analysis of this tree. As for the offices, each of them is smaller branches, and their related functional departments can be understood as leaves because they have no branches.
In other words, the financial management function of the headquarters that we hope is to reuse it to the subsidiary. The best way is to deal with the accounting management function of the head office and the accounting management function of the subsidiary are the same. This involves a design pattern called the combination pattern.
19.2 Combination mode
Composite: Combining objects into a tree structure to represent a hierarchy of "part-total". Combination mode allows users to be consistent with the use of single objects and combined objects.
Combination mode structure diagram:
Component declares interfaces for objects in the composition, and, where appropriate, implements the default behavior of interfaces shared by all classes. Declares an interface to access and manage subcomponents.abstract class Component{void Add(Component c){};void Remove(Component c){};//Usually, the Add and Remove methods are used to provide the function of adding or removing leaves or leaves void Display(int depth){}}
Leaf represents a leaf node object in the combination, and the leaf node has no child nodes.calss Leaf:Component{override void Add(Component c){};override void Remove(Component c){};}; Since the leaves do not add branches and leaves, it is meaningless to implement it, but doing so can eliminate the difference between leaf nodes and branch node objects at the abstract level, and they have completely consistent interfaces.
Composite defines branch node behavior, is used to store subcomponents, and implements operations related to subcomponents in the Component interface, such as adding Add and deleting Remove.class Composite:Component{children=new List<Component>();//A collection of sub-objects is used to store its subordinate branch nodes and leaf nodes override void Add(Component c){(c)};override void Remove(Component c){(c)};override void Display(int depth){foreach(Component component in children){(depth + 2)}}//Show its branch node name and traverse its subordinate level}
Client code, which can operate objects that combine components through the Component interface. class Main{Composite root = new Composite("root");//Spanning tree, according to root, two leaves of LeafA and LeafB grow on the root}
19.3 Transparent and safe ways
The tree may have countless branches, but it only takes Composite to repeatedly implement the tree structure. Some questions,Why do Leaf also have Add and Remove? Can't leaves grow branches?
Yes, this method is called the transparent method, that is, declare all methods to manage sub-objects in Component, including Add, Remove, etc. In this way, all subclasses that implement the Component interface have Add Remove. The advantage of this is that there is no difference between leaf nodes and branch nodes to the outside world, and they have completely consistent behavioral interfaces. But the problem is also obvious, because the Leaf class itself does not have the functions of Add() and Remove() methods, so it is meaningless to implement it.
So what if you don’t want to do such useless work? That is, the Leaf class does not use Add and Remove methods, is it OK?
Of course, it's OK,Then a safe way is needed, that is, if the Add and Remove methods are not declared in the Component interface, the Leaf of the subclass does not need to implement it, but declares all methods used to manage subclass objects in Composite. In this way, the problem mentioned just now will not occur. However, because it is not transparent enough, the leaves and branches will not have the same interface, and the client calls need to make corresponding judgments, which causes inconvenience.
19.4 When to use Combination Mode
Where is the best place to use the combination mode?
When you find that the requirements are structures that reflect part and overall hierarchy, and you want users to ignore the differences between combined objects and individual objects, and use all objects in the combined structure in a unified manner, you should consider using the combination mode.
The TreeView controls that have been used before are typical combination mode applications.There are also custom controls, that is, combining some basic controls, by programming, write into a customized control, such as using two text boxes and a button to write a custom login box control. The base class of all web controls is, and the Control base class has Add and Remove methods, which is a typical combination mode application.
19.5 Company Management System
Company abstract class or interface abstract class Company{void Add(Company c);//Add void Remove(Company c);//Remove void LineOfDuty();//Perform duties, different departments need to perform different duties}
Specific company class, implement interface branch nodes class ConcreteCompany:Company{children = new List<Company>();override void LineOfDuty(){foreach(Company component in children){();}}}
Human Resources Department and Accounts Department Leaf Node class HRDepartment:Company{override void LineOfDuty(){"{0}Employee Recruitment Training Management", name}}
Accounting Department Leaf Nodeclass FinanceDepartment:Company{override void LineOfDuty(){"{0}Company Accounting Income and Expenditure Management", name}}
Client calls class Main{ConcreteCompany root = new ConcreteCompany("Beijing Corporation");(new HRDepartment("Health Resources Department of the Corporation"));(new FinanceDepartment("Health Management Department of the Corporation"));ConcreteCompany root = new ConcreteCompany("Shanghai East China Branch")}
19.6 Benefits of Combination Mode
The combination model thus defines a class hierarchy that includes basic objects such as the Human Resources Department and the Finance Department, as well as group objects such as branches and offices.The basic objects can be combined into more complex combination objects, and this combination object can be combined, so that it continues to recurse continuously. In the client code, any place where the basic objects are used can be used.
Also, users don’t have to worry about whether to process a leaf node or a combination component, so there is no need to write some choice judgment statements to define the combination. To put it simply, the combination pattern allows customers to use combination structures and individual objects consistently.
Chapter 20 Want to leave? Can! Buy tickets first - iterator mode
20.1 Buy tickets by car, no matter who you are!
Every ticket seller is doing an important thing, which is to traverse everyone in the carriage and not let a passenger who does not buy tickets. This is also a reflection of a design pattern. Iterator mode.
20.2 Iterator mode
Iterator mode: Provides a method to access individual elements in an aggregate object in sequence without exposing the internal representation of the object.
The ticket seller, no matter whether you are coming up with a person or a property (baggage), whether you are a Chinese or a foreigner, whether you are an internal employee, or even a thief who is about to be captured, as long as you are a passenger coming to take the bus, you must buy a ticket.By the same token, when you need to access an aggregated (collection) object and need to traverse whatever these objects are, you should consider using the iterator pattern.In addition, ticket sellers can sell tickets from the front to the rear of the car, and can also sell tickets from the rear to the front of the car. That is to say, when you need to traverse the gathering in multiple ways, you can consider using the iterator mode.
Since no matter what the passenger is, the conductor's approach is always the same. They start from the first one, who is the next one, whether it is the end, and who is currently sold. These methods are done every day, that is, they provide unified interfaces such as the beginning, the next one, whether it is the end, and which one is currently sold.
20.3 Iterator implementation
Iterator pattern structure diagram:
abstract class Iterator{object First();object Next();boolean IsDone();object CurrentItem();} Iterative abstract class is used to define abstract methods such as obtaining the starting object, obtaining the next object, determining whether it reaches the end, and the current object, and unifying the interface.
abstract class Aggregate{abstract Iterator CreateIterator();//Create iterator}Aggregate (collection) abstract class
class ConcreateIteator:Iterator{ConcreteAggregate aggregate;//Define a specific aggregation (collection object); override object First(){return aggregate[0]}}
class ConcreateAggregte:Aggregate{private List<object> items = new List<object>;//Declare a List generic variable to store aggregate (collection) objects, which can also be implemented with ArrayList; override Iterator CreateIterator(){return new ConcreateIteator(this)}}
Client class Main{ConcreteAggregate a = new ConcreateAggregte();//Bus, that is, gathering (collection) objects a[0]=foreigner; a[0]=thief;//New passengers, that is, object array Iterator i = new ConcreateIteator(a);//Ticket seller appears, first look at who is getting on the bus, that is, declare the iterator object}
Why use the concrete iterator ConcreteIterator to implement abstract Iterator? I feel that there is no need for abstraction here, wouldn’t it be better to directly access ConcreteIterator?
That's because I didn't pay attention to the benefits of an iterator just now. When you need to traverse aggregation (collection), you can consider using the iterator mode. In fact, ticket sellers do not have to sell tickets from the front to the rear of the car, but can also traverse from the back to the front.
20.4 .NET iterator implementation
Not only .NET, Java also provides ready-made iterator framework-related interfaces, just need to implement them. For example, ArrayList:
We do not need to explicitly refer to iterators, but the system itself still implements traversal through iterators. In general, the iterator pattern separates the traversal behavior of the collection object and abstracts an iterator to take responsibility. This will not expose the internal structure of the collection, and allow external code to transparently access the data inside the collection.
20.5 Iteration master
Chapter 21 Some categories also require family planning - singleton model
Class 21.1 Family planning is also required
Every time you click on the menu item in the toolbox, a new toolbox form is generated, but in fact, you only hope it appears once, or simply not.
21.2 Determine whether the object is null
This is actually not difficult to do. Just judge whether this FromToolbox has been instantiated.
Why do you need to declare the FormToolbox object when you click the button? You can completely put the declared work into the global variables of the class. This way you can determine whether this variable has been instantiated.
21.3 It is not about not having a child, it is your own responsibility
The couple already have a child. Who will be responsible for whether they have a second child?
Of course, they are responsible for it themselves. If they are born and violate the country's policies, it is also their own reasons.
Think about this scenario again: the leader asked his subordinates if he had submitted the report, and the subordinates could say that he had submitted it long ago, so the leader nodded with satisfaction, and the subordinates could also say that there was still some content left and they had not written it. They quickly handed over it, and the leader frowned and said he should hurry up. At this time, whether this report has not been submitted, who will judge?
Of course, it is the subordinate's own judgment, because the subordinates know best whether the report has been submitted, and the leader only needs to ask.
Similarly, whether the toolbox FromToolbox is instantiated is judged in the code of the MDI main form From1. This is not logical. Form1 should just notify the startup of the toolbox. As for whether the toolbox form has been instantiated, the toolbox itself should judge.
Instantiation is actually the process of new, but the question is how can I let others not use new?
Yes, if the construction method is not changed, it is impossible to prevent others from not using new. So we can change the constructor of this class directly to private.
All classes have construction methods. If no encoding is used, the system will generate an empty construction method by default. If there is a construction method that displays the definition, the default construction method will fail. So as long as the toolbox class constructor is written as private, external programs cannot instantiate it with new.
For external code, new cannot be used to instantiate it, but we can completely rewrite a public method called GetInstance(). The purpose of this method is to return a class instance, and in this method, we make a judgment on whether there is an instantiation. If not instantiated. This instance is new by calling private constructors. The reason why it can be called is that they are in the same class, the private method can be called.
In this way, the client no longer considers whether it needs to be de-instified, but gives all the responsibilities to the classes that should be responsible for handling. In fact, this is a very basic design pattern: singleton pattern.
21.4 Singleton Mode
Singleton: Ensure that a class has only one instance and provides a global access point to access it.
Usually we can make an object accessed by a global variable, but it cannot prevent you from instantiating multiple objects. One of the best ways is to let the class itself be responsible for saving its only instance. This class guarantees that no other instance can be created, and it can provide a way to access the instance.
Singleton pattern structure diagram:
Singleton class, defines a GetInstance operation, allowing customers to access its unique instance. GetInstance is a static method that is mainly responsible for creating its own unique instance.
What are the benefits of singleton mode besides ensuring a unique instance?
For example, singleton mode is because the Singleton class encapsulates its only instance so that it can strictly control how and when the customer accesses it.Simply put, it is controlled access to the only instance.
Why do you feel that singleton is a bit like a static method of a practical class, such as the Math class in the .net framework, which has very data calculation methods. What is the difference between the two?
They are indeed very similar, and practical classes usually use privatized construction methods to avoid instances. But they are still many different:
1. For example, a practical class does not save state, and only provides some static methods or static properties for you to use, while a singleton class is stateful.
2. Practical classes cannot be used to inherit polymorphism, and although singletons have unique instances, they can have subclasses to inherit.
3. Practical classes are just collections of method attributes, while singletons have unique object instances.
21.5 Singleton when multithreaded
You need to pay attention to some details. For example, in a multi-threaded program, multiple threads are simultaneously accessing the Singleton class and calling the GetInstance() method may cause multiple instances to be created.
The process can be processed with a lock.Here we need to explain the meaning of lock statement (using synchronized in java), lock is to ensure that when one thread is in the critical section of the code, another thread does not enter the critical section. If other threads try to enter locked code, it will wait (i.e. blocked) until the object is released.
class Singleton{private static Singleton instance;private static readonly object syncRoot = new object();//Create a static readonly process helper object while the program is running singleton GetInstance(){lock(syncRoot){if(instance == null){instance = new Singleton();}}//The only one thread can enter the part of the program that has locked it at the same time}}
This code makes the object instance created by the first thread of the person entering, and future threads will no longer create object instances when entering. With lock, it is ensured that simultaneous access in a multi-threaded environment will not cause multiple instances to be generated.
Why not lock (instance) directly, but create a syncRoot to lock?
Because when locking, I don’t know whether the instance instance has been created or not, so how to lock it. But there is another problem, that is, lock is required every time the GetInstance method is called, which will affect performance, so this class is improved.
21.6 Double lock
class Singleton{public static Singleton GetInstance(){if(instance == null)//First determine whether the instance exists, and then lock it if it does not exist, then add it {lock(syncRoot){if(instance == null){instance = new Singleton();}}}}}
Now, we don’t have to let the thread lock every time, but just lock the instance when it is not created. It can also ensure the security of multi-threading. This practice is called Double-Check Locking.
There is a problem,It has been determined outside whether the instance instance exists. Why do we need to make a judgment on whether the instance instance exists in the lock?
After careful analysis, if the instance exists, you will return directly, there is no problem.When the instance is null and two threads call the GetInstance() method at the same time, they will all be judged by the first instance==null. Then due to the lock mechanism, only one of these two threads enters, and the other is waiting in line outside. One of them must enter and come out before the other can enter. At this time, if there is no judgment on whether the second-level instance is null, the first thread creates an instance, while the second thread can still continue to create a new instance, which does not achieve the purpose of a singleton.
21.7 Static initialization
Since the constructor is private, the Singleton class cannot be instantiated outside the class itself; therefore, the variable refers to the only instance that can exist in the system.
Variables can only be allocated during static initialization or in class constructors. This static initialization method is to instantiate yourself when it is loaded, so it is vividly called the Hungry Singleton class.
The original singleton pattern processing method was to instantiate itself when it was referenced for the first time, so it was called a lazy singleton class.
Hungry man-lazy man, what are the main differences between them?
Because the Hungry style, that is, the static initialization method, is an object that is instantiated as soon as the class is loaded, so system resources must be occupied in advance.
Lazy guys will face the security problem of multi-threaded access, and double locking is required to ensure security.So which method to use depends on the actual needs. From the perspective of C#, the singleton class of Hungry Man style is enough to meet our needs.
Chapter 22 When will the mobile phone software be unified? - Bridge mode
22.1 Why can't I play your game
Your mobile phone is from M brand, mine is from N brand. Logically, you cannot play the games here.
The mobile phones and models of the same brand are different, and the software is basically compatible. Unfortunately, the software cannot be integrated together in different brands.
However, in the field of computers, it is completely different. For example, with the Window operating system, all PC manufacturers do not need to pay attention to software, and software manufacturers do not need to pay too much attention to hardware, which is very beneficial to the overall development of computers.
22.2 Tightly coupled program evolution
Mobile phone hardware software and PC hardware software actually contain two completely different ways of thinking.
If I now have an N-brand mobile phone and it has a small game, and I want to play the game, how should the program be written?
Write a game of this brand and then call it with the client.
Games in N-brand mobile phones: class HandsetNGame{void Run(){//Run N-brand mobile game}}
Client code: class Main{game = new HandsetNGame();();}
Now there is another M-brand mobile phone, and there are also mini games, and the client can also call it.
Both brands have games. I feel that from the perspective of object-oriented thinking, there should be a parent mobile brand game, and then let N and M brand mobile games inherit from it, so that the same operation method can be achieved.
Since both mobile phones need address book function, both N and M brands have added the function of adding, deleting, modifying and checking address book. How to deal with it?
That means that the parent category should be a mobile phone brand, with mobile phone brand M and mobile phone brand N, and each subcategory has a address book and a game subcategory.
Code structure diagram:
Mobile phone
Mobile phone brand category: class HandsetBrand{void Run(){}}
Mobile phone brand N and mobile phone brand M: class HandsetBrandM: HandsetBrand{void Run(){}}
Subordinate address book and game category
Game category of mobile phone brand M: class HandsetBrandMGame: HandsetBrandM{override void Run(){//Run M brand mobile game}}
Address book category of mobile phone brand M: class HandsetBrandMAddressList:HandsetBrandM{override void Run(){//Run M brand mobile phone address book}}
Mobile phone brand N same as above
If we need to add an MP3 music playback function for each brand now, how to do it?
Then add a subcategory below each brand. If a new mobile phone brand S comes, which also has games, address book, and MP3 music player functions, then you have to add the mobile phone brand S category and three subordinate function subcategories, which is obviously a bit troublesome.
I feel that I have been designing with object-oriented theory. First, there is a brand, and then multiple brands abstract a brand abstraction category. For each function, they inherit their respective brands. Or, if you don’t classify it from the perspective of brand, but from the perspective of mobile phone software, what’s wrong with this?
There are problems, it's like having a new hammer, everything looks like a nail. But in fact, inheritance can cause trouble in many situations。For example, the inheritance relationship of an object is defined at compile time, so the implementation inherited from the parent class cannot be changed at runtime.. The implementation of a subclass has a very close dependency with its parent class, so that any changes in the implementation of the parent class will inevitably lead to changes in the subclass. When you need to reuse subclasses, if the inherited implementation is not suitable for solving new problems, the parent class must be overwritten or replaced by other more suitable classes.This dependency limits flexibility and ultimately limits reusability.
If new brands or new functions are continuously added according to the previous inheritance structure, there will be more and more categories.
In object-oriented design, we also have a very important design principle, which is the synthesis (combination)/aggregation and multiplexing principle. That is, use object synthesis (combination)/aggregation first rather than class inheritance.
22.3 Synthesis (combination)/polymerization and multiplexing principle
The principle of synthesis (combination)/aggregation and reuse: Try to use synthesis/aggregation, and try not to use class inheritance.
Composition (Composition, also translated into composition) and aggregationThey are all related special types。
Aggregation represents a weak ‘own’ relationship, which reflects that object A can contain object B, but object B is not part of object A;
Synthesis is a strong ‘ownership’ relationship, reflecting the strict relationship between part and the whole, and the life cycle of part and the whole is the same.
For example, a geese have two wings. The wings and geese are part and whole, and their life cycles are the same, so the geese and wings are a synthetic relationship.
Geese are flocking animals, so each geese belongs to a flock of geese, and a flock of geese can have multiple geese, so geese and geese are in a convergence relationship.
The advantage of the synthesis/aggregation principle is that prioritizing synthesis/aggregation of objects will help you keep each class encapsulated and concentrated in a single task. This way, class and class inheritance levels will remain small in scale and are unlikely to grow into uncontrollable behemoths.
In the example just now, you need to learn to consider the issue using the responsibilities of the object rather than the structure. In fact, the answer lies in the difference between mobile phones and PCs we talked about before.
Mobile phones are different brands of companies, each making their own software, just like the current design, while PCs are hardware manufacturers making hardware, software manufacturers making software, and only when combined is a machine that can be used.
In fact, functions such as games, address books, and MP3 music playback are all software. If we can separate them from the mobile phone, we can greatly reduce the unreasonable situation of excessive changes in the face of new demands.
The meaning is that there should be a mobile phone brand (hardware) abstract class and a mobile phone software (software) abstract class, so that different brands and functions can be inherited from them separately. In this way, new brands or new functions need not be affected by other classes.
The relationship between mobile phone brands and mobile software is that mobile phone brands include mobile phone software, but the software is not part of the brand, so they are an aggregation relationship.
22.4 Loosely coupled program
Mobile phone software abstract class
Mobile software: abstract class HandsetSoft{abstract void Run();}
Games, address books and other specific categories
Mobile Game: class HandsetGame:HandsetSoft{override void Run(){//Run mobile games}}
Mobile phone brand (hardware)
Mobile phone brand: abstract class HandsetBrand{HandsetSoft soft;void SetHandsetSoft(HandsetSoft soft){ = soft;}//The brand needs to pay attention to the software, so you can install the software in the machine (set mobile phone software) in case of running abstract void Run();}
Brand N Brand M Specific Category
Mobile phone brand N: class HandsetBrandN: HandsetBrand{override void Run(){();}}
Client call:
Now if you want to add a function, such as the MP3 music playback function, then just add this class and the ground. No other class will be affected. The number of classes increases only one.
If you want to add S brand, you only need to add a brand subcategory. The number is also one, and will not affect other types of changes.
This obviously also conforms to our previous principle of openness-closing. Such a design obviously will not modify the original code, but will just extend the class. But the deepest thing is the principle of synthesis/aggregation and reuse, that is, the use of synthesis or aggregation of objects is preferred rather than class inheritance. The charm of aggregation is infinite. In comparison, inheritance is indeed easy to cause unnecessary trouble.
Blind use of inheritance will of course cause trouble, and what is the main reason?
It should be that inheritance is a strongly coupled structure. The parent class changes, and the child class must change.
Therefore, when we use inheritance, we must consider using it when it is an is-a relationship, rather than using it at any time. A design pattern that should be called bridge mode in this example today.
22.5 Bridge mode
Bridge mode: Separate abstract parts from their implementation parts so that they can all be varied independently.
Here we need to understand what is separation from abstraction and its implementation. This does not mean that abstract classes are separated from derived classes, because this has no meaning. Implementation refers to the abstract class and its derived class that implements its own objects.As for the example just now, mobile phones can be classified by brand or by function.
The core meaning of the bridge mode is to separate these implementations and make them change separately. This allows each implementation to change without affecting other implementations, thereby achieving the purpose of responding to changes.
22.6 Basic code of bridge mode
It is still difficult to understand what the bridge pattern says is to separate the abstract part from its implementation part.In a common understanding, it is to realize that the system may have multiple angles, and each category may change. So separate these multiple angles and allow them to change independently to reduce the coupling between them.
In other words, when we find that we need to classify objects from multiple angles, and using inheritance alone will cause a large number of classes to increase and cannot meet the open-closing principle, we should consider using the bridge mode.
I feel that as long as I truly understand the design principles in depth, many design patterns are actually just application of principles, and perhaps I am using design patterns without realizing them.
22.7 I want to develop a good game
Chapter 23 Thoughts caused by grilled lamb kebabs—Command Mode
23.1 Eat grilled lamb skewers
Which is more profitable, guerrilla kebabs or kebabs? In fact, it is easier to get along with the streets of guerrillas, and it can also correspond to a very important design model.
23.2 Barbecue stand vs. Barbecue shop
There are too many people eating kebabs at the barbecue stall, and they all hope to get the kebabs as soon as possible. The barbecue boss is alone, so it is a bit confusing. Once there are too many people, he may not remember who has paid the money, how many strings are needed, whether he needs to be spicy, etc.
Due to the tight coupling between the customer and the kebab boss, it is easy to make mistakes, easily confusing, and easily picky.
This is actually the tight coupling between the behavior requester and the behavior implementer. We need to record which person needs a few skewers, whether there are special requirements (spicy or not), whether they have paid for it, whoever comes first and whoever comes later, this is actually equivalent to recording the request, which should be a log.
Then if someone needs to return the request, or asks the roasted pork to be re-grilled, this is actually equivalent to revoking and redoing.
Therefore, when queuing requests or recording request logs, and supporting revocable operations, tight coupling between the behavior requester and the behavior implementer is not suitable.
In fact, we don’t have to know who the roast pork is, and we don’t even have to see his noodles. We just need to tell the waiter who received us what we want. He can record our request and then he will inform the barbecue master to make it.
Moreover, because the request we made is actually our order for meat, there are very detailed requirements on it. All customers have this order. The barbecue chef can operate in order, without being confused or forgotten.
Also, if you want to change the order, you just need to notify the waiter, and the waiter will mark the book and then inform the barbecue master. This is actually an operation to cancel the action. With records, the final settlement will not be wrong. This is to use a waiter to decouple the handling benefits of customers and barbecue chefs.
23.3 Tight coupling design
Implementation of roadside kebabs: class Barbecuer{void BackMutton(){//kebabs}}
Client call: class Main(){boy = new Barbecure();//The client program is tightly coupled with the kebab. Although it is simple, it is extremely rigid and has many hidden dangers}
If there are too many users and too many requests, it will easily become messy. There is a need to add a waiter here.
You should know that whether it is grilled lamb kebabs, grilled chicken wings, or other barbecue, these are the behaviors of kebabs, that is, their methods. The specific methods are implemented internally, so we don’t have to worry about it. But for the waiter, he actually issued a command based on the user's needs, saying: Some people want ten mutton skewers, and some people want two chicken wings. These are all commands.
It means to write the methods in the kebab class into multiple command classes, so they can be requested by the waiter. These commands are actually almost the same style, so you can generalize an abstract class and just call the abstract command.. What specific command is, that is, what is baking, will be decided by the customer.
23.4 Loose coupling design
Code structure diagram:
Abstract command class
abstract class Command{Barbecure receiver;public Command(Barbecure receiver){ = receiver;}//Abstract command class, you only need to determine who the kebab is abstract void ExcuteCommand();//Execute the command}
Specific command class
Grilled lamb kebab command: class BakeMuttonCommand:Command{override void ExcuteCommand(){();//Specific command class, execute specific behaviors when executing commands}}
Roasted Chicken Wing Command: class BakeChickenWingCommand:Command{override void ExcuteCommand(){();}}
Waiter class: class Waiter{Command command;//Set order void SetOrder(Command command){ = command;//Waiter class, don’t care what barbecue the user wants. Anyway, it’s all commands. Just record the order and then notify the kebab to execute}//Notify the execution of void Notify(){();}}
Client implementation: class Main{//Preparation before opening a store Barbecuer boy = new Barbecuer(); Other omitted...}
The basic code has been implemented, but there are several problems.
First, the real situation is not that when the user orders a dish, the waiter will notify the kitchen to make one. That is unscientific. It should be that the waiter will notify the production one after ordering the barbecue;
Second, if the chicken wings are gone at this time, it shouldn’t be the customer who will determine whether there is still there. The customer who doesn’t know if there is one. It should be the waiter or kebab vetoing the request;
Third, what barbecue or drinks the customer ordered need to be recorded for charges, including later statistics;
Fourth, it is entirely possible for customers to consider canceling some meat skewers that have not been made because they ordered too many meat skewers.
These problems need to be resolved.
23.5 After loose coupling
Waiter category:
class Waiter{orders = new List<Command>();//Add a container to store specific commands; Set order void SetOrder(Command command){if("Chicken wings are gone"){("Chicken wings are gone, order other barbecue");}else{(command);("Add order + time");//Record the barbecue log ordered by the customer to settle accounts and collect money};//Cancel order void CancelOrder(Command command){(command)};//Notify all void Notify(){foreach(Command cmd in orders){();//Notify kitchen production based on the barbecue order ordered by the user}}}
This is the famous command mode.
23.6 Command Mode
Command mode:Encapsulate a request into an object, allowing you to parameterize customers with different requests; queue or log requests, and support revocable operations.
Command mode structure diagram:
Command class, used to declare the interface to perform operations. abstract class Command{Receiver receiver;Command(Receiver receiver){ = receiver;} abstract void Execute();}
ConcreteCommand class, bind a receiver object to an action, and call the corresponding action of the receiver to implement Execute. class ConcreteCommand:Command{ConcreteCommand(Receiver receiver){base(receiver)};override void Execute(){();})}
Invoker (waiter) category, requiring the command to execute this request. class Invoker{Command command;void setCommand(Command command){= command};void ExceutCommand(){();}}
Receiver (BBQ Master) category, know how to implement a request-related operation, any class may act as a receiver. class Receiver{void Action(){//Execute request}}
Client code, create a specific command object and set its receiver. class Main{r = new Receiver();c = new ConcreteCommand(r);i = new Invoker();(c);();}
23.7 Command mode function
Summarize the advantages of command mode.
First, it can design a command queue more easily;
Second, when needed, it can be easier to log commands;
Third, the party receiving the request is allowed to decide whether to reject the request;
Fourth, revocation and redoing of requests can be easily achieved;
Fifth, since adding new specific command classes does not affect other classes, adding new specific command classes is very despicable.
The most critical advantage is that the command mode separates the object that requests an operation from the object that knows how to perform an operation.
Is it necessary to implement the command mode when encountering similar situations?
This is not necessarily true. For example, the command mode supports the undo/restore operation function, but it is not clear whether the command mode should be implemented when this function is needed?
Actually, it should not be implemented. Agile development principles tell us not to add guess-based, practically unwanted features to our code. If you are not sure whether a system needs a command mode, don't rush to implement it. In fact, it is not difficult to implement this mode when needed. It is only meaningful to refactor the command mode when functions such as undo/restore operations are really needed.
Chapter 24: Do you have to criticize the boss for the salary increase? ——Responsibility chain model
24.1 Boss, I want a salary increase
The formalities are about to go through the full probation period, and the salary increase is raised. The manager asked the manager to find the human resources director, and the director went to the general manager.
24.2 Preliminary salary increase code
Whether it is a salary increase or a leave request, it is an application. There should be application categories, application contents and application quantity.
Application: class Request{string requestType;//Application category string requestContent;//Application content int number;//Quantity}
Then the manager, director, and general manager are all managers.
Manager: class Manager{string name;void GetResult(string managerLevel,Request request){if(managerLevel=='Manager'){}else if(managerLevel=='Director'){}else if(managerLevel=='General Manager'){}}}
Client: class Main{jinli = new Manager('1');zongjian = new Manager('2');//Two managers request=new Reqeust()//Salary increase request;('Manager', request)request=new Reqeust()//Leave request;('Manager', request)}
The manager type has a long result method and there are too many branch judgments, which is actually a very bad design. Because if you add other management categories, such as project managers, department managers, etc. That means that everyone needs to change this class. This class bears too many responsibilities, violates the principle of single responsibility, adds new management categories, and needs to modify this class, which violates the principle of openness-closing.
It needs to be reconstructed. It just mentioned that management categories may be added, which means that it is easy to change here. These corporate manager categories can be made into subclasses of managers, which can use polymorphism to resolve the rigidity brought about by branches.
How to solve the problem of the function of the manager not having the right to report to the director and the director not having the right to report to the general manager again?There is a certain relationship between them, and the user's request is passed until the request can be resolved. Here is a behavioral design pattern, responsibility chain pattern is referenced.
24.3 Responsibility Chain Model
Chain of Responsibility: Allows multiple objects to have the opportunity to process the request, thereby avoiding the coupling relationship between the sender and the receiver of the request. Connect the object into a chain and pass the request along the chain until an object processes it.
The client that makes this request here does not know which object in it finally handles the request, so that system changes can dynamically reorganize and assign responsibilities without affecting the client.
Responsibility chain model structure diagram:
Handle class, defines an interface for processing requests.
abstract class Handle{Handle successor;void setSuccessor(Handle successor){=successor;}//Set the successor abstract void HandleReqeust(int request);//Abstract method for handling requests}
ConcreteHandler class, a specific processor class, handles the request it is responsible for, and can access its successor. If the request can be processed, it will be processed, otherwise the request will be forwarded to its successor.
ConcreteHandler1, when the number of requests is between 0 and 10, it has permission to process, otherwise it will be transferred to the next one.
class ConcreteHandler1:Handle{override HandleReqeust(int request){if(request>=0 && request <10){//handle request}else if(successor != null){(request);//Transfer to the next }}}
ConcreteHandler2, when the number of requests is between 10 and 20, there is permission to process, otherwise it will be transferred to the next one.
class ConcreteHandler2:Handle{override HandleReqeust(int request){if(request>=10 && request <20){//handle request}else if(successor != null){(request);//Transfer to the next }}}
Client code submits a request to the specific processor object on the chain.
class Main{h1 = new ConcreteHandler1();h2 = new ConcreteHandler2();(h2);//Set the responsibility chain and the next home int[]reqeusts = {2,5,8,13,27};foreach(int request in requesteusts){(request);//Come to submit requests to the minimum processor, and different amounts are handled by the authorized processor}}
24.4 Benefits of the Chain of Responsibility
The most important thing is that when a client submits a request, the request is passed along the chain until there is a ConcreteHandler object responsible for handling it.
The advantage of doing this is that the request does not need to be managed by any object to handle it, anyway, the request will be processed.
This makes neither the receiver nor the sender have clear information from each other, and the objects in the chain themselves do not know the structure of the chain. The result is that chains of responsibilities simplify the interconnection of objects, which only need to keep a reference to their successors, without keeping all of its candidate recipients. This greatly reduces the coupling degree.
Since the structure of the chain is defined on the client, that is, the structure that processes a request can be added or modified at any time. Enhanced flexibility in assigning responsibilities to the target.
This is indeed very flexible, but be careful, it is very likely that a request cannot be processed at the end of the chain, or it cannot be processed because it is not configured correctly, so it needs to be considered comprehensively in advance. This is like mailing a letter in reality, but it cannot be delivered in the end due to the wrong address.
As for the example just now, there are two most important points.One is that you need to set up each specific manager in advance which category is his boss, that is, set up successors.。Another point is that you need to make a judgment when each specific manager processes the request, whether it is possible to process the request, or you must shirk responsibility and transfer it to the successor to handle it.。
In fact, it is to break down the branches of the manager class written now into each specific manager class, and then use the successors set in advance to realize the permissions for request processing.
24.5 Rebuilding of salary increase code
Let’s first transform this manager class, and at this time it will become the abstract parent class, which is actually a Handler;
Code structure diagram:
Manager: abstract class Manager{string name;Manager superior;//Administrator's superior void SetSuperior(Manager superior){ = superior;} The key method is to set the manager's superior abstract void ReqeustApplications(Reqeust requests);//Application request}
The manager class can inherit this manager class, and you only need to rewrite the method of the application request.
Manager: class CommonManager: Manager{override void ReqeustApplications(Reqeust requests){if(=='Application for leave'&&<=2){//Approved}else{if(superior != null){(request);//The rest of the applications need to be transferred to the superior}}}}}
The director class also inherits the manager class:
All the authority of the general manager needs to be handled:
Since the original manager class was changed to an abstract class and three concrete classes, the flexibility between classes was greatly increased at this time. If we need to extend the new manager category, we just need to add subclasses.
Note:I often see such codes, a series of similar behaviors, but the data or behaviors are different. For example, a bunch of checkers, what happens if they succeed or fail; or a bunch of object builders, each constructing part of the data. When encountering this kind of scenario, I always like to define a general interface. The incoming parameter is the complete parameter to be verified/constructed, and the outgoing parameter is the success/failure mark or void. Then many implementers implement this interface separately, and then use a collection to string the pile of behaviors together. Finally, traverse this set, execute each part of the logic in serial or parallel.
The benefits of doing this are:
Many common codes can be implemented in the base class of the responsibility chain atomic object;
The code is clear and the principle of opening and closing. Whenever a new behavior occurs, you only need to define the implementation class of the row and add it to the collection;
Provides a basis for parallelism.
24.6 Successful salary increase
Chapter 25 The world needs peace—the intermediary model
25.1 The world needs peace
The intermediary mode is also called the mediator mode. In fact, it means an intermediary or a mediator.
Because of the different interests of representatives among countries, conflicts are inevitable, but if there is such an organization composed of representatives of each country, it is used to maintain international peace and security and solve international economic, social, cultural and humanitarian problems,This is the United Nations Organization。It is the role of a mediator and an intermediary.
The relationship between countries is similar to the relationship between different objects, which requires objects to know all other objects. Although splitting a system into many objects can usually increase reusability, the surge in interconnectedness between objects will reduce its reusability.Why is this happening?
Because a large number of connections make it impossible for an object to work without the support of other objects, and the system appears as an indivisible whole, it is very difficult to make any major changes to the behavior of the system.
To solve such a problem,It is necessary to apply the Dimit rule that I have mentioned before.If two classes do not have to communicate directly with each other, then the two classes should not interact directly. If one of the classes needs to call a method of another class, the call can be forwarded through a third party. Here, it means that relationships can be made between countries through the United Nations intermediary without direct communication.
Through the intermediary object,The mesh structure of the system can be transformed into a star structure centered on the intermediary. Each specific object no longer interacts with another object through direct connection, but interacts with another object through the intermediary object.。
The design of the intermediary object allows the system structure to not cause a lot of modification work due to the introduction of new objects.
25.2 Intermediary Model
Mediator mode: Use a mediator object to enclose a series of object interactions. The intermediary makes the objects need not be explicitly referenced to each other, thereby loosely coupling them and can independently change their interactions.
Intermediary model structure diagram:
College is called abstract colleagues;
ConcreteColleague is a specific colleague class, Each specific colleague only knows his own behavior, and does not understand the interfaces of other colleagues, but they all know the intermediary object.
Mediator is an abstract intermediary, defines the interface between colleague object and mediator object.
ConcreteMediator is a specific mediator object, the method to implement abstract class, it needs to know all specific colleague classes, receive messages from specific colleagues, and issue commands to specific colleague objects.
Mediator class abstract mediator class: abstract class Mediator{abstract void Send(string message,Colleague colleague);//Define an abstract message sending method, get colleague object and send message}
Colleague class abstract colleague class: abstract class Colleague{Mediator mediaotr;Colleague(Mediator mediaotr){=mediatr;//Construction method, obtain the mediator object}}
ConcreteMediator class specific mediation class: class ConcreteMediaotr:Mediator{ConcreteColleague1 Colleague1{set{colleague1 = vlaue;}ConcreteColleague2 Colleague2{set{colleague2=value;}}override void send(){if(colleague==colleague1){(message);}else{(message;)//Rewrite the method of sending information, make selection judgments based on the object, and notify the object}}}}
ConcreteColleague1 and ConcreteColleague2 and other similar objects: class ConcreteColleague1:Colleague{//Construct ConcreteColleague1(Mediator mediaotr){mediatr;}void Send(string message){(message,this);// When sending messages, it is usually sent by the intermediary}void Notify(string message){('Colleague1 gets the message'+message);}}
Client call: class Main{//Omitted}
Due to the Mediator, ConcreteColleague1 and ConcreteColleague2 are actually done through intermediaries when sending messages and receiving messages, which reduces the coupling between them.
Here I have a question. The United States and Iraq are both countries. It’s enough to have one country abstract class and two specific country categories. But is the United Nations Mediator or ConcreteMediator?
This depends on whether it is possible to expand the target of intermediary in the future. For example, in addition to the Security Council, the United Nations also has the International Labor Organization, UNESCO, World Health Organization, etc., so the Mediator should be a United Nations agency, and the Security Council is a specific intermediary.
If there is no expansion, Mediator can be combined with ConcreteMediator into one.
25.3 Security Council intermediary
United Nations organization class, equivalent to Mediagor class
United Nations organization class: abstract class UnitedNations{abstract void Declare(string message,Country colleague);//Declaration}
National category, equivalent to College category
Country class: abstract class Country{United Nations mediator;Country(United Nations mediaotr){ = mediaotr;}}
American class, equivalent to ConcreteColleague1 class
American class: class USA: Country{USA(UnitedNations mediaotr){base(mediaotr)};void Declare(string message){(mess,this);//Declaration}void GetMessage(string message){('U.S. gets the other party'+message);}}
Iraqi class, equivalent to ConcreteColleague2 class
Iraqi class: class Iraq: Country{Iraq(UnitedNations mediaotr){base(mediaotr)};void Declare(string message){(mess,this);//Declaration}void GetMessage(string message){('Iwiq gets the other party'+message);}}
UN Security Council, equivalent to ConcreteMediator class
UN Security Council: class UnitedNationsSecurityCouncil:UnitedNations{USA colleague1;Iraq colleague2;//United States set{colleague1=value;}//Iraq set{colleague2=value;}//United Nations Security Council understands all countries, so it has the object attributes of the United States and Iraq override void Declare(string message,Country colleague){if(colleague==colleague1){(message);//Rewrite the declaration method to realize communication between two objects}else{(message);}}}}
Client class: omitted
There is a question to think aboutThe most critical issue is that the ConcreteMediator class must know all the ConcreteColleagues, Although such a design can reduce the coupling between the ConcreteColleague classes, this in turn makes the ConcreteMediator too much responsibility, and if something goes wrong, the entire system will have problems.
25.4 Pros and cons of intermediary model
Yes, if something goes wrong with the UN Security Council, it will certainly have an impact on the world.Therefore, the intermediary model is easy to apply in the system and is also easy to misuse in the system. When the system has a complex object group with many-to-many interactions, don’t rush to use the intermediary model, but first reflect on whether your system is designed to be reasonable. Summarize the advantages and disadvantages of the intermediary model.
Advantages of intermediaries:firstThe emergence of Mediator reduces the coupling of each College, so that each College class and Mediator can be independently changed and reused. For example, any country's changes will not affect other countries, but will only change with the Security Council.Second,Because the objects are abstracted in collaboratively, the mediation is taken as an independent concept and encapsulated in an object, so the objects of interest are transferred from the respective behaviors of the objects to their interactions, that is, to look at the system from a more macro perspective.
Disadvantages of intermediaries:As mentioned earlier, the specific intermediary type ConcreteMediator may become very complex because of the increasing number of ConcreteColleagues, and it is not easy to maintain. Since ConcreteMediator controls centralization, interaction complexity is turned into the complexity of the mediator, which makes the mediator more complex than any ConcreteColleague. The advantages of the intermediary model come from centralized control, and its disadvantages are also it. It should be considered clearly when using it.
The intermediary mode is used in the Form or Wdb website program in Windows applications written in .NET. Aspx is a typical intermediary.
For example, a calculator program, it has menu controls, text controls, multiple button controls and a Form form, and the communication between each control is done through the Form form.Because each control class code is encapsulated, their instances will not know the existence of other control objects. For example, clicking the number button to display numbers in the text box, according to the previous idea, you should write code to assign values to the Text attribute of the TextBox class instance in the Button class, causing the two classes to be coupled, which is obviously very unreasonable.. But the actual situation is that they all have event mechanisms, and the execution of events is completed in the code of Form. That is to say, the interaction of all controls is mediated by Form and operates various objects. This is a typical intermediary mode application.
The intermediary pattern is generally used in situations where a group of objects communicate in a well-defined but complex way, such as the form Form object or web page aspx I just obtained, and the situation where I want to customize a behavior distributed in multiple classes but do not want to generate too many subclasses.
Chapter 26 Don’t do it stupidly if you have many projects - Xiangyuan Mode
26.1 Don’t be stupid if you have many projects
Private owners build websites and have 100 similar merchant customers, basically all of which are information release, product display, blog messages, forum functions, etc. Could you apply for 100 spaces and use 100 databases?Then copy it 100 times with similar code?If there are bugs or new requirements, the maintenance volume will be terrible.
Today's large blog websites and e-commerce websites, each blog or merchant can also be understood as a small website, but how do they do it?
Use the different user ID numbers to distinguish different users. The specific data and templates can be different, but the code core and database are shared.
How to share an instance?
26.2 Enjoy the Yuan mode
Flyweight: Use sharing technology to effectively support a large number of fine-grained objects.
Xiangyuan model structure diagram:
Flyweight class, it is the superclass or interface of all specific virgin classes, Through this interface, Flyweight can accept and act on external states.
abstract class Flyweight{abstract void Operation(int extrinsicstate);}
ConcreteFlyweight inherits the Flyweight superclass or implements the Flyweight interface and adds storage space to the internal state.
class ConcreteFlyweight:Flyweight{override void Operation(int extrinsicstate){("Specific Flyweight:"+extrinsicstate);}}
UnsharedConcreteFlyweight refers to those Flyweight subclasses that do not require sharing.. Because Flyweight interface sharing is possible, it does not force sharing.
class UnsharedConcreteFlyweight:Flyweight{override void Operation(int extrinsicstate){("Non-shared specific Flyweight"+extrinsicstate);}}
FlyweightFactory is a Xiangyuan factory used to create and manage Flyweight objects.It is mainly used to ensure that Flyweight is shared reasonably. When a user requests a Flyweight, the FlyweightFactory object provides a created instance or creates a (if not present).
class FlyweightFactory{Hashtable flyweights = new Hashtable();FlyweightFactory(){("X",new ConcreteFlyweight());("Y",new ConcreteFlyweight();)}//According to the client request, obtain the generated instance Flyweight GetFlyweight(String key){return ((Flyweight)flyweights[key];)}}
Client code:
class Main{int extrinsicstate =22;//External state of code f = new FlyweightFactory();fx = ("x");(--extrinsicstate);(--extrinsicstate);uf = new UnsharedConcreteFlyweight();(--extrinsicstate);}
There is a question: FlyweightFactory returns an object that has been generated based on the customer needs, but must it be a case of the object instance?
In fact, it is not necessary. You can do nothing when initializing. When needed, you can determine whether the object is null to decide whether it is instantiated.
There is another question, why does UnsharedConcreteFlyweight exist?
This is because although we need to share objects to reduce memory loss in large segmentation times, it may not be shared at some time. Then the UnsharedConcreteFlyweight subclass at this time is necessary, which can solve the problem of not requiring shared objects.
26.3 Website sharing code
Website abstract class:abstract class WebSite{abstract void Use();}
Specific website category: class ConcreteWebSite:WebSite{ConcreteWebSite(String name){=name;} override void Use(){("WebCategory""+name)}}
Website factory category: class WebSiteFactory{flyweights = new Hahstable();//Get the website classification and determine whether this object exists. If it exists, it will be returned directly. If it does not exist, instantiate it and return WebSite GetWebSiteCategory(string key){if(!(key)(key,new ConcreteWebSite(key)); return ((WebSite)flyweights[key]);)}//Get the total number of website classifications and get the number of instances int GetWebSiteCount(){return ();}}
Client Code: class Main{f = new WebSiteFactory();//Instantiate the website object for product display WebSite fx = ("Product Display");();//Share the object generated above, no longer instantiate WebSite fy = ("Product Display");();//Blog and other categories take this as an example}}
This writing basically realizes the purpose of sharing objects in the Xiangyuan model. That is to say, no matter how many websites you build, as long as it is a product display, it is the same, and as long as it is a blog, it is also the same, but there is a problem with this. The websites you build for the company are not from one company, and their data will not be the same, so at least they should all have different accounts. What should I do?
In fact, writing like this does not reflect the differences between objects, but only reflects the parts they share.
26.4 Internal and external states
The shared part inside the Xiangyuan object and that will not change with the environment can be called the internal state of the Xiangyuan object, and the state that changes with the environment and cannot be shared is the external state.
In fact, the Enjoy Yuan model can avoid a lot of very similar overheads. In programming, it is sometimes necessary to generate a large number of fine-grained class instances to represent data. If you can find that these instances are basically the same except for a few parameters, you can sometimes be greatly reduced in the number of classes that need to be instantiated. If those parameters can be moved outside the class instance and passed them during method calls, the number of individual instances can be greatly reduced through sharing.
That is to say, the state required for executing Flyweight in the executable mode is internal or external. The internal state is stored in the ConcreteFlaweight object, while the external object should be considered to be stored or calculated by the client object. When the operation of the Flyweight object is called, the state is passed to it.
For example, the client's account is an external state and should be processed by a special object.
Code structure diagram:
User class, the client account used for the website, is the external status of the website class: class User{string name;//provides get-set}
Website abstraction class:abstract class WebSite{abstract void Use(User user);//User method requires passing user object}
Specific website category:slightly
Website factory category:slightly
Client code: class Main{f = new WebSiteFactory();WebSite fx = ("Product Display"");(new User("Jiaojiao");fy = ("Product Display");(new User("Laotong"));//Blog Rule}
Although different users use the website, there are actually only two website instances. This way, the internal and external states can be coordinated.
Because of the Xiangyuan mode, even if you take over the needs of 100 websites, as long as the requirements are the same or similar, your actual development code is the classification type. For the server, it occupies very little hard disk space, memory, and CPU resources, which is indeed a good way.
26.5 Enjoy Yuan mode application
When should we consider using the Xiangyuan mode in reality?
If an application uses a large number of objects, and a large number of these objects cause great storage overhead, you should consider using it; in addition, most of the states of the object can be external. If the external state of the object is deleted, many group objects can be replaced by relatively few shared objects. At this time, you can consider using the Encyclopedia mode.
In actual use, what effect can the Xiangyuan mode achieve?
Because of the Encyclopedia mode, with shared objects, the total number of instances is greatly reduced. If there are more shared objects, the more storage savings will be, and the savings will increase with the increase of shared state.
What are the specific uses of the Xiangyuan model?
In fact, in .NET (including java), string string uses the Flyweight mode. Give an example. (object objA,object objB) method is used to determine whether objA and objB are the same instance, and the return value is the bool value.
string title = "Dayan Design Pattern"; string title = "Dayan Design Pattern";((titleA, titleB)); The return value is true, these two strings are the same instances.
If you need to create a new string object every time you create a string object, the memory overhead will be very high.. So if the string object titleA is created for the first time, the next time I create the same string titleB is just pointing its reference to the Dahua design pattern, which realizes the Dahua design pattern sharing in memory.
Although the Xiangyuan model is often an underlying design model, it is also applicable in reality.For example, in the development of casual games, such as Go, Go, Checkers, etc., they all have Dali chess pieces objects., After analysis, what are their internal and external states?
Go and Go are only black and white, and checkers have slightly more colors, but they are not very different, so the color should be the internal state of the chess piece, and the difference between each chess piece is mainly the difference in position, so the orientation coordinates should be the external state of the chess piece.
In some cases, the number of objects may be too large, resulting in runtime resource and performance loss. So how we avoid a large number of fine-grained objects without affecting the client program is a question worth thinking about. The Encyclopedia mode can use sharing technology to effectively support a large number of fine-grained objects. However, using the Encoding mode requires maintaining a list of all Encodings that record the system, which itself requires resource consumption. In addition, Encoding mode makes the system more complex. In order to use objects that can be shared, some state needs to be externalized, which complicates the logic of the program. Therefore, the Encyclopedia mode should be worth using when there are enough object instances available for sharing.
Chapter 27 Actually, you don’t understand the boss’s heart – Interpreter mode
27.1 Actually, you don’t understand the boss’s heart
When the boss privately praises a certain employee, it is probably because you have more tasks that you need to complete recently.
Usually the boss says that an employee is an ordinary employee, but in fact, what he means is that the employee is not smart enough and has insufficient work ability.
It would be great if there was a translator or interpreter, so that it would save you from having to think more about each speech.
27.2 Interpreter Mode
Interpreter mode: Given a language, defines a representation of its grammar (grammar), and defines an interpreter that uses this representation to interpret sentences in the language.
What the interpreter pattern needs to solve is that if a particular type of problem occurs at a high frequency, it may be worth expressing individual instances of the problem as sentences in a simple language.This allows you to build an interpreter that solves the problem by interpreting these sentences. For example,We often search for matching characters in strings or determine whether a string meets the format we stipulate. At this time, we usually use regular expressions.
In fact, browsers like IE and Firefox are actually explaining HTML grammar, converting HTML tag text downloaded to the client into a web page format to display it to the user. However, writing a browser program is of course much more complicated.
Interpreter mode structure diagram:
AbstractExpression (abstract expression),Declare an abstract interpretation operation, which is shared by all nodes in the abstract syntax tree:
abstract class AbstractExpression{abstract void Interpret(Context context);}
TerminalExpression (terminal expression),Implement interpretation operations associated with terminators in grammar. Implementing the interface required in abstract expressions is mainly an interpret() method. Each terminator in the grammar has a specific terminating expression corresponding to it.
class TerminalExpression:AbstractExpression{override void Interpret(Context context){("Terminal Interpreter")}}
NotterminalExpression (nonterminal expression),Implements interpretation operations for non-terminal characters in grammar. Each rule R1, R2, and Rn in the grammar requires a specific non-terminal expression class. Implement the interpret operation by implementing the interpret() method of abstract expressions. The explanation operation calls the above mentioned instance variables representing the individual symbols in R1, R2, and Rn in a recursive manner.
class NotinalExpression:AbstractExpression{override void Interpret(Context context){("Non-terminal interpreter");}}
Context, contains some global information outside the interpreter
class Context{string input;//Provide get-set string output;//Provide get-set}
Client Code, construct an abstract syntax tree that represents a specific sentence in the language defined by this grammar (grammar). Call the explanation operation.
class Main{context = new Context();list = new List<AbstractExpression>();(new TerminalExpression());(new NoterminalExpression();)for(AbstractExpression exp in list){(context)}}
27.3 Interpreter Mode Benefits
Using the interpreter mode is like you develop a programming language or script for yourself or others to use.
The interpreter mode is to use a mini language to express the problems that the program needs to solve, and write it into a mini program to express specific problems in a mini language.
Usually when there is a language that needs to interpret execution, and you can represent sentences in that language as an abstract syntax tree, you can use the interpreter pattern.
What are the benefits of interpreter mode?
Using the interpreter pattern means that the grammar can be easily changed and extended because the pattern uses classes to represent grammar rules, and you can use inheritance to change or extend the grammar. It is also easier to implement grammar because the implementation of classes that define individual nodes in the abstract syntax tree is generally similar, and these classes are easy to write directly.
The interpreter mode is to convert a sentence into actual command program execution. Without the interpreter mode, it could have been analyzed, butThe method of inheriting abstract expressions is convenient for the expansion and maintenance of grammar due to the principle of dependence.
There are also shortcomings in the interpreter mode,The interpreter pattern defines at least one class for each rule in the grammar, so grammars containing many rules may be difficult to manage and maintain. It is recommended that when the grammar is very complex, use other techniques such as syntax analysis programs or compiler generators to handle it.
27.4 Music Interpreter
The phones I used to use had the function of editing ringtones. By entering some simple alphanumeric numbers, you can make the phone make music. Using the rules defined in the QB or mobile phone instruction manual to write a music program is a grammar that allows QB or mobile phone to translate into specific instructions to execute.
27.5 Music Interpreter Implementation
Code structure diagram:
Performance content class (context):class PlayContext{//Play text string text;//Providing get-set}
Expression class (AbstractExpression):abstract class Expression{//Interpret(PlayContext context){//Omitted} abstract void Excute(string key,double value);//Abstract method execution, different grammar subclasses, different execution processing}
Note class (TerminalExpression):class Note:Expression{override void Excute(string key,double value){string note="";switch(key){case "C":note=1;// means that if the obtained key is C, play 1(do), and if D, play 2(Re)}}}
Note class (TerminalExpression):class Scale:Expression{override void Excute(string key,double value){string scale="";switch(value){case 1:scale="bass";// means that if the obtained key is O and value is 1, the bass will be played, 2 is the midpoint, and 3 is the treble}}}
Client Code: class Main{//Omitted}
Now we need to add a grammar, which is the performance speed. The requirement is that T represents speed, in milliseconds, T1000 represents one second per beat, and T500 represents half a second per beat.
First, add a subclass of the expression called Sonic. Then add a case branch to the client's branch judgment.
Sonic class: class Speed:Expression{override void Excute(string key,double value){string speed;if(value < 500){speed="fast"}}}
In fact, this example cannot represent the full picture of the interpreter pattern, because it only has terminator expressions, but does not have subclasses of non-terminator expressions, because if you want to truly understand the interpreter pattern, you need to study other examples. In addition, this is a console program. If you give the sound files of all the piano keys, in MP3 format, you can use the Media Player control to write a real music language interpreter. As long as you compile such sentences according to the simplified score, you can let the computer simulate the piano playing.
27.6 Predictions are like gods
Chapter 28 Men and Women - Visitor Mode
28.1 Men and women
Visitor pattern refers to an operation that acts on each element in a certain object structure. It allows you to define new operations that act on each element without changing the class of each element.
The main reason why there are so many comparisons between men and women is that there are only two types of gender in humans: men and women. Humans are divided into men and women, so there are so many comparisons.
28.2 The simplest programming implementation
class Main{("When a man succeeds, there is probably a great woman behind him");("When a woman succeeds, there is probably an unsuccessful man behind him");//Other comparisons}
28.3 Simple object-oriented implementation
Humans are abstract categories of men and women.
abstract class Person{string action;//Provide get-set abstract void GetConlusion();//Get conclusion or reaction}
Men: class Man:Person{//Get conclusion or reaction override void GetConlusion(){if(action=="Success"){//There is most likely a great woman behind it}};}
Female human: class Woman:Person{//Get conclusion or reaction override void GetConlusion(){if(action=="Success"){//There is most likely an unsuccessful man behind it}};}
Client: class Main{persons = new List<Person>;man1 = new Man();="Success";(man1);//Other omitted}
There is still a problem with the above code. If...else... is very annoying. If you want to add a marriage status now, then both classes need to add branch judgments. If these states are written, what should I do?
28.4 Implementation of the mode
State abstract class:
abstract class Action{//Get the conclusion or reaction of man abstract void GetManConclusion(Man concreteElementA);//Get the conclusion or reaction of woman abstract void GetWomanConclusion(Woman concreteElementB);}
Human abstract class:
abstract class Person{//Accept abstract void Accept(Action visitor//It is used to obtain state objects);}
The key here is that people are divided into men and women. The gender classification is stable, so in the state category, two methods can be added to the men's reaction and women's reaction. The number of methods is stable and will not change easily. There is an abstract method in the human abstract class that accepts it, which is used to obtain state objects. Each specific state inherits the state abstract class and implements two reaction methods.
Specific status category:
Success Status Class:class Success:Action{override void GetManConclusion(Man man1){("When a man succeeds...");}override void GetWomanConclusion(Woman woman1){("When a woman succeeds...")}}
Failed status class: class Failing:Action{//The same as the above success status, omitted}
Love status category: class Amativeness:Action{//The same as the above success status, omitted};
Men and women:
Men:class Man:person{override void Accept(Action visitor){(this);}}
Female human:class Woman:person{override void Accept(Action visitor){(this);}}
Here we need to mention a kind of dispatching technology used. First, in the client program, the specific state is passed as a parameter to the man class to complete a dispatch, and then the man class calls the method in the specific state as a parameter, and at the same time passes himself (this) as a parameter. This completed the second dispatch.
Double dispatch means that the operation performed depends on the type of request and the type of two recipients. The acceptance method is a double dispatch operation. The operation it is performed depends not only on the specific state of the state class, but also on the category of people it visits.
Since the object structure class always requires the comparison between men and women in different states, we need an object structure class to traverse men and women for different states and get different adaptations.
Object structure class: class ObjectStructure{elements = new List<Person>();//Add void Attach(Prrson element){(element);}//Remove void Detach(Person element){(element);//View and display void Display(Action visitor){foreach(person p in elements){(visitor);//Travel method}}}}}
Client class: class Main{o = new ObjectStructure();(new Man());(new Woman());//Include the man and woman to compare to the object structure;//Reaction v1 = new Success();(v1);//View the reactions of men and women in various states;//Reactions in failure v2 = new Failing();(v2);//Other omitted}
What are the benefits of doing this?
If we want to increase the marriage status to test the reactions of men and women, due to the use of double dispatch, we only need to add a state subclass, which can be called on the client side without changing the code of any other class.
This perfectly reflects the principle of openness-closing, and this model is called the and visitor mode.
28.5 Visitor Mode
Visitor mode: represents an operation that acts on each element in an object structure. It allows you to define new operations that act on each element without changing it.
Visitor pattern structure diagram:
Declare a Visit operation for each class of ConcreteElement in this object structure:
abstract class Visitor{abstract void VisitConcreteElementA(ConcreteElementA a);bstract void VisitConcreteElementB(ConcreteElementB b);}
For specific visitors, implement each operation declared by Visitor. Each operation implements part of the algorithm, and the algorithm fragment is a class corresponding to the object in the structure:
class ConcreteVisitor1{void VisitConcreteElementA(ConcreteElementA a){};void VisitConcreteElementB(ConcreteElementB b){}};
class ConcreteVisitor2{void VisitConcreteElementA(ConcreteElementA a){};void VisitConcreteElementB(ConcreteElementB b){}};
Define an Accept operation, which takes a visitor as the parameter:
abstract class Element{void Accept(Visitor visitor);};
Specific elements to implement Accept operations
class ConcreteElementA{void Accept(Visitor visitor){};void OperatorA(){}};
class ConcreteElementB{void Accept(Visitor visitor){};void OperatorB(){}};
Object structure class, which can enumerate its elements, can provide a high-level interface to allow visitors to access its elements
class ObjectStructure{}
Here, Element is our human beings, while ConcreteElementA and ConcreteElementB are men and women, Visitor is the status category we write, and ConcreteVisitor is the status of success, failure, love, etc. As for ObjectStructure, it is an object structure class.
The main reason why there are so many comparisons between men and women is that humans have only two types of gender: men and women. And this is also the premise that the visitor mode can be implemented.
What is this premise?
If human gender is not just male and female, but can have multiple genders, it means that abstract methods in state classes cannot be stable. Each time one category is added, a method needs to be added to the state class and all its subordinate classes, which does not conform to the principle of open-closing.
That is, the visitor mode is suitable for systems with relatively stable data structures., it frees the coupling between the data structure and the operations acting on the structure, so that the set of operations can evolve relatively freely.
What is the purpose of visitor mode?
The purpose of visitor mode is to separate processing from the data structure. Many systems can be separated by algorithms and data structures. If such systems have relatively stable data structures and algorithms that are easy to change, it is more appropriate to use the visitor mode because the visitor mode makes it easier to increase the algorithm operation.On the contrary, if the data structure objects of such systems are easily changed and new data objects are often added, it is not suitable to use the visitor mode.
In fact, the advantage of visitor mode is that it is easy to add new operations, because adding new operations means adding a new visitor. Visitor pattern centralizes the relevant behavior into a visitor object.
Usually ConcreteVisitor can be developed separately and does not have to be written with ConcreteElementA or ConcreteElementB. Because of this, ConcreteVisitor can improve the independence between ConcreteElement. If a processing action is designed as a method of ConcreteElementA and ConcreteElementB, every time you want to add new processing to expand the function, you have to modify ConcreteElementA and ConcreteElementB. This is the code I wrote before, adding judgments on success and failure states to men and women, resulting in a tight coupling between processing methods and data structures.
The disadvantage of visitors is that it becomes difficult to use new data structures.
One of the four Gof authors said: Most of the time you don’t need visitor mode, but once you need visitor mode, that’s really needed it. In fact, it is difficult for us to find a situation where the data structure does not change, so there are not many opportunities to use the visitor pattern. This is why it is discussed when it comes to comparing men and women, because data structures like human gender will not change.
28.6 Basic code of visitor mode
Visitor class declares a Visit operation for each class of ConcreteElement in this object structure.
abstract class Visitor{abstract void VisitConcreteElementA{ConcreteElementA concreteElementA};abstract void VisitConcreteElementB{ConcreteElementB concreteElementB};}
The ConcreteVisitor1 and ConcreteVisitor2 classes are specific access classes to implement each operation declared by the Visitor. Each operation implements part of the algorithm, and the algorithm fragment is a class corresponding to the object in the structure.
class ConcreteVisitor1: Visitor{override void VisitConcreteElementA(ConcreteElementA ca){(().Name is accessed); override void VisitConcreteElementB(ConcreteElementB cb){(accessed);}}}
class ConcreteVisitor2:Visitor{//The code is similar to the above class, omitted}
Element class, defines an Accept operation, which takes a visitor as a parameter
abstract class Element{abstract void Accept(Visitor visitor);}
ConcreteElementA and ConcreteElementB classes, specific elements, implement Accept operations.
class ConcreteElementA:Element{override void Accept(Visitor visitor){(this);//Make full use of dual dispatch technology to achieve separation of processing and data structure}; void OperatorA(){};//Other related methods}
class ConcreteElementB:Element{//The code is similar to the above class, omitted}
ObjectStructure class, which can enumerate its elements, can provide a high-level interface to allow visitors to access its elements.
class ObjectStructure{elements = new List<Element>();void Attach(Element element){(element);}void Detach(Element element){(element);}void Accept(Visitor visitor){for(Element e in elements){(visito);}}}
Client code:
class Main{ObjectStructure o = new ObjectStructure();(new ConcreteElementA());(new ConcreteElementB());ConcreteVisitor1 v1 = new ConcreteVisitor1();ConcreteVisitor2 v2 = new ConcreteVisitor2();(v1);(v2);}
28.7 It is not enough to compare with the top, but it is more than below.
The ability and complexity of visitor mode is a double-edged sword, and only consider using it when you really need it. Many programmers show their object-oriented abilities to the people or are addicted to the pattern, and often misuse this pattern, so they must understand its applicability carefully.
Summary: The most commonly used design mode 1. Factory method mode 2. Appearance mode 3. Observer mode 4. Strategy mode 5. Adapter mode
Summary of development procedures steps (personal opinion):
1. Determine the language used by the development program
2. Understand the characteristics of the development language
3. Taking using java as an example: The characteristic of java language is object-oriented
4. Since it is object-oriented, you must know the object-oriented characteristics
5. Three major Object-oriented features: Encapsulation, Inheritance, Polymorphism
6. Understand the use of these characteristics and what is their main function
7. Add design pattern ideas before writing programs
8. Before using the design pattern, you need to understand the four principles of the design pattern to ensure the architecture of the design program.
9. Four principles of design patterns: single responsibility principle (reducing the responsibility of the class), open-closing principle (opening for extensions - closing for modification), dependency inversion principle (programming for interfaces, not implementation programming), Dimit principle (emphasizing loose coupling between classes)
10. Start the architecture
See attachment for the 23 design patterns codes: -----------------
-
-
-
-
-
!