Have you thought about what the design pattern is? With this article you can see why design patterns are so important, using several examples of Python to show why design patterns are needed and how to use them.
What is the design pattern?
Design patterns are summarized, optimized, and reusable solutions to a number of programming problems that we often encounter. A design pattern is not as direct to our code as a class or a library. Instead, the design pattern is more advanced, a method template that must be implemented in a particular situation. Design patterns do not bind to specific programming languages. A good design pattern should be able to be implemented in most programming languages (if not all, depending on the language feature). Most importantly, the design pattern is also a double-edged sword, if the design pattern is used in inappropriate circumstances will cause disaster, and then bring endless trouble. However, if the design pattern is used in the right place at the right time, it will be your savior.
At first, you would think that "model" is a sensible way of thinking specifically to solve a particular problem. Yes, it does seem to be one of the most versatile and flexible solutions that can be viewed from a different perspective by working with many people. Maybe you've seen or solved these problems, but your solution is probably not as complete as the model.
Although they are called "design patterns," they are not closely related to the "Design" field. The design pattern is different from the traditional analysis, design and implementation, in fact, the design pattern has a complete idea rooted in the program, so it may appear in the analysis stage or higher level design stage. Interestingly, because the design pattern is embodied in the program code, it may make you think that it will not appear before the specific implementation phase (in fact, you do not realize that you are using a specific design pattern until you enter a specific implementation phase).
Patterns can be understood through the basic concepts of programming: adding an abstraction layer. To abstract one thing is to isolate any specific detail, so that the purpose is to separate the unchanging core parts from other details. When you find that some parts of your program are constantly changing for some reason, and you don't want the parts of those changes to cause other parts of the changes, then you need to think about the design methods that don't change. Doing so will not only make your code more maintainable, but it will make your code easier to understand and reduce development costs.
The tricky part of designing an elegant, maintainable program is finding what I call a "change vector" (where "vector" refers to the maximum gradient change direction (maximum gradient), not a container class). It means finding the most important part of the changes in the system, or in other words, finding out where the biggest cost of the system is. Once you have discovered the vector of change, you can design your program around this focus.
So the purpose of the design pattern is to separate the variable parts of the code. If you look at the problem this way, you'll see multiple design patterns immediately. For example, object-oriented inheritance (inheritance) can be viewed as a design pattern (though it is implemented by the compiler). It allows the performance of different behaviors (parts of change) through the same interface (invariant parts). A combination can also be considered a design pattern because it allows you to change the objects of the implementation class and their behavior by dynamic or static means.
Another common example of a design pattern is an iterator. The iterator has existed with the use of the For loop since the advent of Python, and has been explicitly a feature of the Python2.2 version. An iterator hides the concrete implementation inside the container, providing a way to access each element in the container object in turn. So, you can use generic code to manipulate each element of a sequence without having to pay attention to how the sequence is built. So your code can be effective for any object that can generate an iterator.
Here are three of the most basic design patterns:
- Structured patterns, often used to deal with relationships between entities, enable these entities to work better together.
- Creates a pattern, provides an instantiated method, and provides the appropriate object creation method for the appropriate situation.
- Behavioral patterns, which are used to communicate in different entities, providing easier and more flexible communication methods for communication between entities.
Why do we use design patterns?
Theoretically, the design pattern is a better solution to the program problem. Countless programmers have encountered these problems, and they use these solutions to deal with these problems. So when you have the same problem, why think about creating a solution instead of being ready and proven to be effective?
Example
Assuming that there is a task now that requires you to find an effective way to combine two classes that do different things, in an existing system where these two classes are used in many different places, it is extremely difficult to remove the two classes or change existing code. Not only that, changing existing code can lead to a lot of testing, because in such a system that relies on a large number of different components, these changes always introduce new bugs. To avoid these problems, you can implement a variant of the Strategy mode (strategy pattern) and Adapter mode (Adapter pattern), both of which can handle this problem well.
Class Strategyandadapterexampleclass ():
def __init__ (self, context, Class_one, class_two):
self.context = Context
Self.class_one = class_one
self.class_two = Class_two
def operation1 (self):
if Self.context = = "Context_for_class_one":
self.class_one.operation1_in_class_one_context ()
else:
Self.class_ Two.operational_in_class_two_context ()
It's simple, isn't it? Now let's take a closer look at the strategy model.
Policy Mode
A policy pattern is a behavior-related design pattern that allows you to determine the action of a program at run time based on the specified context. You can encapsulate different algorithms in two classes and determine exactly which policy to execute when the program is run.
In the above example, the policy is determined based on the value of the context variable when instantiated. If the value of the given context variable is "Class_one", the Class_one is executed, or the class_two is executed.
Where do I use it?
Suppose you are now writing a class that can update or create a new user record, receive the same input parameters (such as name, address, mobile number, etc.), but invoke the corresponding update or create method depending on the situation. Of course, you may use a if-else to judge the problem, but what if you need to use this class in different places? Then you have to keep rewriting if-else judgments. Why not simply specify the context to solve the problem.
Class User ():
def create_or_update (self, name, address, Mobile, Userid=none):
if UserID:
# It means the User D OESN ' t exist yet, create a new record
else:
# It means the user already exists, just update based in the given use Rid
The general policy pattern involves encapsulating the algorithm into another class, but if so, that class is too wasteful. Remember not to rote templates, to grasp the core concept of flexible flexibility, the most important thing is to solve the problem.
Adapter Mode
The adapter pattern is a structured design pattern that allows new uses for a class through different interfaces, making it possible for systems that use different invocations to use this class.
It also allows you to change the input parameters received through the client class to accommodate the relevant functions of the person being fitted.
How to use it?
Another place to use the adapter class is the wrapper (wrapper), which allows you to wrap an action into a class that can then be reused in the appropriate case. A typical example is when you create a domain class for a table cluster, you can encapsulate all of the same actions for different tables into an adapter class instead of calling these different actions individually. Not only will you be able to reuse all the actions you want, but you don't need to rewrite the code when you use the same action in different places.
Compare these two implementations:
Scheme without adapters
Class User (object):
def create_or_update (self):
Pass
Class Profile (object):
def create_or_update ( Self):
pass
user = user ()
user.create_or_update () Profiles
= profile ()
Profile.create_or_ Update ()
If we need to do the same thing in different places, or reuse the code in different projects, we need to knock it over again.
solutions that use the wrapper class
Let's see how we can counter it:
Account_domain = account ()
Account_domain. NewAccount ()
In this case, we use a wrapper class to implement the account domain class:
Class User (object):
def create_or_update (self):
Pass
Class Profile (object):
def create_or_update ( Self):
Pass
class account ():
def new_account (self):
user = user ()
user.create_or_update () Profile
= profile ()
profile.create_or_update ()
In this way, you will be able to use the account domain when you need it, and you can also wrap other classes into the domain class.
Factory mode
A factory pattern is a created design pattern that functions as its name: This is a class that produces object instances like factories.
The primary purpose of this pattern is to encapsulate object creation processes that may involve many classes into a single method. Outputs the specified object instance through the given context.
when do you use it?
The best time to use Factory mode is when you need to use multiple variants of a single entity. For example, you have a button class that has many variations, such as a picture button, an input box button, or a Flash button. Then you will need to create different buttons on different occasions, at which point you can create different buttons through a factory.
Let's start by creating three classes:
Class Button (object):
html = ""
def get_html (self): return
self.html
class Image (Button):
html = "
Then create our factory class:
Class Buttonfactory ():
def create_button (self, Typ):
Targetclass = Typ.capitalize () return
globals () [ Targetclass] ()
Globals () will return all global variables in a dictionary, so Targetclass = Typ.capitalize () will get the class name (Image, input, or flash) through the passed-in Typ string, and Globals () [ Targetclass] takes the class name to the class (see the Meta Class), and Globals () [Targetclass] () creates an object of this class.
We can use the factory class like this:
Button_obj = buttonfactory ()
button = [' Image ', ' input ', ' flash '] for the
B in button:
print Button_ Obj.create_button (b). get_html ()
The output will be the HTML attribute for all button types. This way you can specify different types of buttons based on different situations and are easy to reuse.
Adorner mode
The adorner pattern is a structural pattern that allows us to add new or additional behavior to an object at run time, depending on the situation.
The goal is to apply an extended function method to a particular object instance, and also to produce an original object without a new method. It allows multiple adorners to be used in one instance, so you don't have instances that are bundled with a single adorner. This pattern is an alternative way of implementing subclass inheritance, which refers to integrating the corresponding functionality from the parent class. Unlike subclass inheritance, which must be added at compile time to behave differently, the adorner allows you to add new behavior as needed at run time.
You can implement an adorner pattern based on the following steps:
- Create an adorner class with the original component class as the base class.
- To add a pointer field to a component class in an adorner class
- Passing a component to the constructor of the adorner class to initialize the component class pointer
- In the adorner class, all the component methods are pointed to the component class pointer, and the
- In the adorner class, override each component method that needs to modify the functionality.
Related Wikipedia (Http://en.wikipedia.org/wiki/Decorator_pattern)
When do you use it?
The best time to use an adorner pattern is when you have an entity that adds new behavior as needed. Suppose you have an HTML link element, a logout link, and you want to make minor changes to the specific behavior based on the current page. In this case, we can use the adorner pattern.
First, set up the decorative pattern we need.
If we are on the homepage and are already logged in, then the logout link is labeled with the H2 tag.
If we are on a different page and have already logged in, then use the underline tag to mark the link
If you are logged in, mark the link with bold.
Once the decoration mode is established, we can start the work.
Class Htmllinks (): Def set_html (self, html): self.html = html def get_html (self): return self.html def Render (self): print (Self.html) class Logoutlink (htmllinks): def __init__ (self): self.html = "<a href=" Logou T.html "> Logout </a>" Class Logoutlinkh2decorator (htmllinks): Def __init__ (self, logout_link): Self.logou T_link = Logout_link self.set_html ("{0}". Format (self.logout_link.get_html ()) def call (self, name, args): SE Lf.logout_link.name (Args[0]) class Logoutlinkunderlinedecorator (htmllinks): Def __init__ (self, logout_link): self.
Logout_link = Logout_link self.set_html ("{0}". Format (self.logout_link.get_html ()) def call (self, name, args): Self.logout_link.name (Args[0]) class Logoutlinkstrongdecorator (htmllinks): Def __init__ (self, logout_link): Sel
F.logout_link = Logout_link self.set_html ("<strong> {0} </strong>". Format (self.logout_link.get_html ()) def call (self, name, aRGS): Self.logout_link.name (args[0]) Logout_link = Logoutlink () is_logged_in = 0 In_home_page = 0 if is_logged_in: Logout_link = Logoutlinkstrongdecorator (logout_link) If In_home_page:logout_link = Logoutlinkh2decorator (logout_link
) Else:logout_link = Logoutlinkunderlinedecorator (logout_link) Logout_link.render ()
Single case mode
A singleton pattern is a created design pattern that ensures that the runtime only has a single instance object for a class and provides a global access point to access the instance object.
Because this globally unique access point "coordinates" the access requests to a single instance object for other objects that invoke the single instance, the single instance variables that these callers see will be the same.
When can you use it?
The single case pattern may be the simplest design pattern, and it provides a unique object of a particular type. In order to achieve this goal, you must control the object generated outside the program. A convenient approach is to take a single object of a private inner class as a single instance object.
class Onlyone:class __onlyone:def __init__ (self, arg): Self.val = Arg de F __str__ (self): return repr (self) + self.val instance = None def __init__ (self, arg): If not Onlyone.instan Ce:OnlyOne.instance = Onlyone.__onlyone (arg) else:OnlyOne.instance.val = arg def __getattr__ (self, NAM E): Return GetAttr (self.instance, name) x = Onlyone (' sausage ') print (x) y = onlyone (' eggs ') print (y) z = onlyone (' SP Am ') print (z) print (x) print (y) print (' x ') print (' Y ') print (' z ') output = ' <__main__.__onlyone instance at 0076b7ac& Gt;sausage <__main__.__onlyone instance at 0076b7ac>eggs <__main__.__onlyone instance at 0076b7ac>spam < __main__.__onlyone instance at 0076b7ac>spam <__main__.__onlyone instance at 0076b7ac>spam <__main__. Onlyone instance at 0076c54c> <__main__. Onlyone instance at 0076daac> <__main__. Onlyone instance at 0076aa3c> ' "'
Because the built-in class is named with a double underline, it is private and cannot be accessed directly by the user. The built-in class contains all the methods you want to place in the normal class and controls its creation through the constructor of the outer wrapper class. When you first create a onlyone, initialize an instance object, and then ignore the request to create the new instance.
Access through a proxy, using the __getattr__ () method to point all calls to a single instance. You can see from the output that although it looks as if you created multiple objects (Onlyone), there is only one __onlyone object. Although there are multiple instances of onlyone, they are proxies for unique __onlyone objects.
Note that the above method does not limit you to creating only one object, which is also a technique for creating a finite pool of objects. In that case, however, you may encounter problems with objects in the shared pool. If this is really a problem, then you can solve the problem by checking in the "check-in" and moving out the "check-out" mechanism for shared object design.
Summary
In this article, I've only listed a few design patterns that I think are very important in programming, and there are many other design patterns that need to be learned. If you are interested in other design patterns, the Wikipedia design pattern section can provide a lot of information. If it's not enough, you can look at gang's design pattern: The basics of reusable object-oriented software, the book is a classic design model.
One last thing: when using design patterns, make sure you're working on the correct problem. As I mentioned earlier, design patterns are double-edged: if used improperly, they can create potential problems and, if properly used, they will be essential.