First, the importance of design in software development
Importance
When we went to college, we always didn't understand why we had to talk about such a theoretical thing, it was just a feeling that it was useless, we wanted to learn something that we could see, and when you graduated you would find that basic knowledge was important, And this knowledge has a commonality is can be separated from the specific technology or problems exist, is a long-term guide us to learn and progress of the important idea, design principles and patterns is the software development of this idea.
Design principles
Let's start by thinking about a problem:
What kind of software is a good software or a programmer how do we evaluate his (her) coding technology?
Let's assume that a project is done independently by a programmer, and that what he does is not only coded so easily, but the code that makes the implementation function is less than 30% of the entire project, and the first thing he does is to have a technical selection and framework design for the whole project (the learning and understanding of the requirements is not considered). The next is to formally write code, in the implementation process and need to test and modify (refactoring), so that is enough? If you can do this you can develop a complete software, but not enough. Software engineering and building a difference between the place is here, the House is not necessary to remove the heavy cover, software development We also need to consider the future iterations and changes, so we have to do the whole structure has a good maintainability, design principles is what? The principle of design is the theoretical basis (or thought) that guides us to achieve this structure.
Design Patterns
Well, design principles have, I also know what principles should be followed, what to do next? How do I apply this principle to practical projects? Design pattern is to solve these problems arise, the white design pattern is our wisdom of the old-timers summed up some of the six design principles to follow the actual application of the object-oriented approach. We have learned that these design patterns can make us more aware of the importance of design principles, and design principles can also help us to remember and flexibly apply various design patterns.
Two or six major design principles
Single principle of responsibility
The English name of the single Responsibility Principle, the SRP, is defined as a single duty: there should never is more than one reason for a class-to-change . (There should be and only one reason for class changes).
The principle is to know how we encapsulate an object (or how to divide and define a class), and the phrase "All things are objects" indicates that everything is an object, and how to define a class of things is entirely up to our own needs. The single principle of responsibility is that we should try to get him to do one thing as much as possible when we define (or divide) a class, and it seems that the principle is simple, but the question is precisely in this simple "one thing", how to differentiate is one thing is a problem? As an example,
/** * 电话接口 * @author PeggyTong * */publicinterface IPhone { //拨打电话 publicvoiddial(String phoneNumber); //通话 publicvoidchat(Object obj); //挂断电话 publicvoidhangup();}
The above interface does not seem to be a problem, it is to do one thing to call (as our cell phone), but if we define a thing here as a simple call, then it is not doing one thing, so that the single responsibility here according to our specific business logic, rather than the smaller the better.
What is the role of a single responsibility?
1. The complexity of classes is reduced, and what responsibilities are defined clearly and clearly.
2. Readability is improved and complexity is reduced, so it improves readability.
3, maintainability improvement.
4, the change causes the risk reduction, one interface modification only has the influence to the corresponding realization class, has no influence to other interfaces.
In fact, in software development, a single responsibility is not only for the class and interface, but also for the definition of the method should be known for the meaning (do one thing).
The principle of the Richter replacement
The first definition: If for each object O1 of type S There are an object O2 of type T
such that for all Programs p defined in terms of T, the behavior of P
is unchanged when O1 are substituted for O2 then S is a subtype of
T. (If for each object of type S O1, there is an object of type T O2, so that all program P defined in T is replaced with O2 for all objects O1, the behavior of the program P does not change, then the type S is a subtype of type T)
The second definition: Functions that user pointers or references base classes must be
Able to use objects of derived classes without knowing
It. (all places referencing the base class must be able to transparently use objects of its subclasses)
The second definition is the clearest, and the popular point is that as long as the parent class can appear where subclasses can appear, and the substitution of subclasses will not produce any errors or exceptions, the consumer may not need to know the parent class or the child class at all. But on the other hand, there are sub-categories where the parent may not be able to adapt.
Seeing this principle, you may wonder why this principle is necessary. The role of a single principle of responsibility can also be understood, this is completely confused what is the use.
To understand the role of this principle, first you need to know that an important feature of object-oriented is called polymorphism, the benefits of polymorphism can be said to be the advantage of the Richter replacement principle, polymorphism can be different subclasses as the same parent class to see, so you can block the differences between subclasses, write generic code, make general programming, To meet the changing needs.
The Richter substitution principle simply tells us what not to do and does not tell us a viable way to inherit, so engineers are beginning to focus on how to ensure the behavior of the object. In 1988, B. Meyer proposed design by contract (contract design) theory. DBC draws on a set of methods to ensure object behavior and its state from a formal approach, the basic concept is simple:
Post-condition:
Before each method call, the method should verify the correctness of the passed parameters, only the correct ability to execute the method, or assume that the caller violates the contract and does not execute. This is called a pre-condition (pre-condition).
Invariant: The object itself has a check condition to verify its state to ensure that the object's nature does not change, which is called invariant (invariant).
We can easily see these traits in Java, and if we do not meet these characteristics, the compiler will tell us the error or the warning.
Dependency Inversion principle
Dependency inversion principle (dependence inversion Principle, DIP), definition: High level modules
Should not depend upon low level modules. Both should depend upon
Abstractions. Abstractions should not depend upon details. Details
should depend upon abstractions.
1, high-level modules should not rely on low-layer modules, both should rely on its abstraction.
2. Abstraction should not depend on details.
3. Details should be dependent on abstraction.
High-rise modules and low-level modules are easy to understand, each logical implementation is composed of atomic logic, the indivisible atomic logic is the low-level module, the re-assembly of atomic logic is the high-rise module. So what is abstraction? What is the detail? In the Java language, abstraction is an interface or abstract class, both of which cannot be instantiated directly; the details are the implementation class, the class that implements the interface or abstract class is the detail, the characteristic is that can be instantiated directly, namely can add a keyword new to produce an object. The behavior of the dependency inversion principle in the Java language is:
1, the dependency between the modules is sent through the abstraction, the realization class does not have the direct dependence relation, its dependence is through the interface or the abstract class produces.
2. Interfaces or abstract classes do not depend on the implementation class.
3, the implementation class depends on the interface or abstract class.
The dependency inversion principle can reduce the coupling between classes, provide the stability of the system, reduce the risk caused by concurrent development, and improve the readability and maintainability of the code.
There are three ways of relying:
1. Constructors Pass dependent objects
package cn.org.sunhome.yldz;//具体的司机publicclass Driver implements IDriver{ private ICar mCar; publicDriver(ICar car){ mCar = car; } @Override publicvoiddrive() { mCar.run(); }}
2. Setter methods Pass dependent objects
package cn.org.sunhome.yldz;//具体的司机publicclass Driver implements IDriver{ private ICar mCar; publicvoidsetCar(ICar car){ mCar = car; } @Override publicvoiddrive() { ifnullreturn; mCar.run(); }}
3, interface declaration of dependent objects (also called interface injection) is the way above
package cn.org.sunhome.yldz;//司机接口publicinterface IDriver { publicvoiddrive(ICar car);}
The dependency inversion principle itself is through the abstraction (interface or abstract class) to make each class or module implementation independent of each other, do not affect each other, to achieve the coupling between the modules, how do we use this rule in the project? Just follow these rules to:
1, each class as far as possible have interface or abstract class, or abstract class and interface both have.
2, the surface type of the variable as far as possible interface or abstract class.
3. No class should derive from a specific class.
4, try not to overwrite the method of the base class.
5, combined with the Richter replacement principle.
Interface Isolation principle
Definition one: Client should not being forced to depend upon interface that they
Don ' t use. (The client should not rely on the interface it does not need) definition two: The dependency of one class to
Another one should depend on the smallest possible
Interface (the dependency between classes should be based on the minimum interface)
The above two definitions can be understood in fact, we in the establishment of the interface as far as possible to refine the interface, and the interface of the method to be as small as possible.
The interface isolation principle is a canonical constraint on an interface that contains the following 4 meanings:
1, the interface should be as small as possible.
2, the interface to high cohesion.
3, customized services.
Sometimes our interfaces need to be customized for each client, and this time the interface should be split.
4, the interface design is limited.
The smaller the design of the interface, the more flexible the system, this is a fact of no dispute, but flexible at the same time it is easy to bring about structural complications, the development difficulty increases, maintainability is reduced, so the design of the interface must pay attention to moderation.
The interface isolation principle is the definition of an interface, as well as the definition of a class, where interfaces and classes are assembled using atomic interfaces or atomic classes whenever possible. However, how this atom is divided is a problem in the design pattern, which can be measured in practice according to the following rules:
1. An interface only serves one submodule or business logic.
2. The public method in the interface is compressed through the business logic.
3, has been contaminated interface, as far as possible to modify, if the risk of change is larger, then use the adapter mode for conversion processing.
4, understand the environment, refuse to follow blindly. Each project or product has specific environmental factors, the environment is different, interface split standards are different, the best interface design is the premise of deep understanding of business logic.
Dimitri Law
The Dimitri rule (law of Demeter, LoD) is also known as the least-knowledge principle (Leak knowledge Principle,
LKP), although the name is different, but the same rule is described: An object should have a minimal understanding of other objects. In layman's words, a class should know the least about the classes it needs to be coupled or called.
If a system conforms to the Dimitri Law, then when one of the modules changes, it will minimize the impact of the other modules, the extension is relatively easy, this is the limit of communication between software entities, the Dimitri law requires limiting the width and depth of communication between software entities. The Dimitri law can reduce the coupling degree of the system, and keep the coupling between classes and classes loosely.
There are several defining forms of the Dimitri Law, including: Don't Talk to strangers, just communicate with your direct friends, in the Dimitri rule, for an object, its friends include the following categories:
(1) The current object itself (this);
(2) An object that is passed into the current object method in the form of a parameter;
(3) The member object of the current object;
(4) If the member object of the current object is a collection, then the elements in the collection are also friends;
(5) The object created by the current object.
Any object, if one of the above conditions is met, is the "friend" of the current object, or "stranger". When applying the Dimitri rule, an object can only interact with a direct friend and not interact directly with strangers, which reduces the coupling of the system, and changes in one object do not affect too many other objects.
The Dimitri law requires us to minimize interaction between objects when designing a system, and if two objects do not have to communicate directly with each other, the two objects should not have any direct interaction, and if one of the objects needs to invoke a method of another object, the call can be forwarded through a third party. In short, it is to reduce the coupling between existing objects by introducing a reasonable third party.
When applying the Dimitri rule to system design, pay attention to the following points: in the division of classes, we should try to create loosely coupled classes, the lower the coupling between classes, the more conducive to reuse, a loose coupling in the class once modified, will not cause too much impact on the associated class, in the class structure design, Each class should minimize the access rights of its member variables and member functions; in the design of a class, a type should be designed to be immutable as long as it is possible; On references to other classes, one object's references to other objects should be minimized.
Opening and closing principle
Definition of opening and closing principle: software entities like classes,modules and functions should beopen for extension but closed for modifications (one software Entities such as classes, modules, and functions should be open to extensions and closed for modification. )
The definition of the open and closed principle tells us very clearly that software entities should be opened to extensions and shut down for modifications, meaning that a software entity should be extended to implement changes rather than modifying existing code. What is a software entity? Software entities include the following sections:
1, the project or software products in accordance with a certain logical rules division of the module.
2, abstract and class.
3, methods.
As long as a software product changes in the life cycle, since change is a given fact, we should try to adapt to this change in design, in order to improve the stability and flexibility of the project, and truly realize "embrace change". The open and closed principle tells us that we should try to achieve change by extending the behavior of the software entity, rather than modifying the existing code to complete the change, which is a principle of constraining the current development design for the future events of the software entity.
We
The key to realizing the opening and shutting principle is "abstraction". The abstraction of all possible behaviors of a system into an abstract bottom layer prescribes the characteristics of the methods that all concrete implementations must provide. As the abstraction layer of system design, it is necessary to foresee all possible extensions so that the abstraction of the system does not need to be modified at any scale, and because one or more new concrete implementations can be exported from the abstraction level, the system's behavior can be changed, so the system design is open to the extension.
Let's take a look at an example
Public interface IBook { //books have names PublicStringGetName();//books have a price Public int GetPrice();//books have authors PublicStringGetauthor();}//Fiction Public class novelbook implements IBook{ PrivateString name;Private intPricePrivateString author; Public Novelbook(String name,intPrice, String author) { This. name = name; This. Price = Price; This. Author = author; }@Override PublicStringGetName() {returnName }@Override Public int GetPrice() {returnPrice }@Override PublicStringGetauthor() {returnAuthor }}//Bookstore Sales Public class bookstore { Private Final StaticArrayList Booklist =NewArrayList ();Static{Booklist.add (NewNovelbook ("Tian Long eight department",3200,"Jin Yong")); Booklist.add (NewNovelbook ("Notre Dame",5600,"Hugo")); Booklist.add (NewNovelbook ("A world of sadness",3500,"Hugo")); Booklist.add (NewNovelbook ("Golden bottle Plum",4300,"Lan Ling smiles and Laughs")); } Public Static void Main(string[] args) {NumberFormat formatter = numberformat.getcurrencyinstance (); Formatter.setmaximumfractiondigits (2); System.out.println ("The books sold in----------------bookstore are recorded as follows------------------"); for(IBook book:booklist) {System.out.println ("book Name:"+ book.getname () +"\ t book author"+ book.getauthor () +"\ t book price"+ Formatter.format (Book.getprice ()/100.0) +"Yuan"); } }}
If the bookstore begins to sell at a discount: All 40 yuan of books 90 percent sales, the other 80 percent sales, for this change, we should how to deal with, there are three ways to solve:
1. Modify the interface
A new Method Getoffprice () is added to ibook, which is designed for discounted processing, and all implementation classes implement the method. However, the result of this modification is that the implementation of the class Novelbook to modify, Bookstroe in the main method is also modified, while the ibook as an interface should be stable and reliable, should not be constantly changed, otherwise the interface as the role of the contract lost performance, therefore, the program is negative.
2. Modify the Implementation class
Modify the methods in the Novelbook class to achieve a discount directly in the GetPrice (), but this is not a good idea, because if the purchasing staff to see the actual price will not be able to view.
3, through the expansion to achieve change
Add a subclass Offnovelbook, overwrite the GetPrice () method, high-level module through the Offnovelbook class to produce objects, complete business changes to the minimum system development, good way!
Brief analysis of six major design principles