GRASP is a combination of the first letters of the general responsibility assessment software patterns:
GENERAL: general, abstract, and widely used;
Responsibility: responsibilities, obligations, and responsibilities;
Assessment: assign responsibility to a module or class;
Software: ComputerCode, Software;
Patterns: rule, template, abstraction or pattern;
GRASP has a great guiding significance for object-oriented system analysis and design, so that we can allocate object responsibilities. First, we need to clarify the meaning of the Object Responsibility.
Responsibility is a contract or obligation between classes. Responsibilities can include behaviors (methods), data, and object creation. They can be divided into two parts: Knowing responsibility-indicating what to know; behavior responsibility-indicating what to do.
Therefore, the formula is: Responsibility = know responsibility + behavior responsibility
Responsibilities include:
(1) Understand private encapsulated data;
(2) understand the associated objects;
(3) understand the transactions that can be derived or computed;
Behaviors and responsibilities include:
(1) perform some behaviors by yourself, such as creating an object or performing a calculation;
(2) initialization in other objects;
(3) control or coordinate activities among other objects.
The object-oriented design process is the process of assigning the responsibility to the object. Note that the responsibility is not a class method, and the class method is used to implement the responsibility. Responsibility allocation can be reflected in the Collaboration diagram or sequence diagram.
For example, in a sales business, there is a payment, and it is a responsibility. Its behavior responsibility indicates the payment, and it needs to create an object for the payment record. It knows the responsibility and must know the payment record-type payment, and how to record and calculate the data in the payment class. The owner of this responsibility should be the sales saled ,:
The information expert mode is the most basic principle of object-oriented design. That is to say, when we design an object (class), if a class can have complete information in a certain aspect that is sufficient to fulfill a certain responsibility, it will be assigned to this class, this is the so-called information expert.
In the example of the shopping cart system, we want each item to appear only once in the shopping cart. If the same item is added to the car, we only need to increase the number of items, add 1 to the original quantity, instead of adding an identical item to the itemarray ),
If so,ProgramIt is necessary to check whether two products are the same. The question is which class should have the responsibility of "detection? According to business rules, two commodities are identified by commodity code, which is described as productid in modeling. Which class will have productid? This is of course the item class.
According to the principle of information experts, the item class should include the responsibility for detection. Therefore, the equals method is rewritten in the item class to check whether two products are the same product. The Code is as follows:
Code
Public Class Item: iformattable
{
// Reequal Method
Public Override Bool Equals ( Object OBJ)
{
If (OBJ = Null )
Return False ;
// Referencing the same object returns true
If (Object. referenceequals ( This , OBJ ))
Return True ;
// Returns false for different object types
If ( This . GetType () ! = OBJ. GetType ())
Return False ;
Item objitem = (Item) OBJ;
// Returns true if the object has the same product ID
If (_ Productid = Objitem. _ productid)
Return True ;
Return False ;
}
}
For the shoppingcar class, it has the responsibility to add 1 to the number of identical products, so it should have the responsibility to check whether the products exist. It can call the indexof method of arraylist in additem to detect the added item. The Code is as follows:
Code
Public Class Arraylistcart: shoppingcart
{
Private Arraylist _ item;
Public Override Void Additme (item)
{
// Retrieve items with the same list
Int Index = _ Item. indexof (item );
// Commodity judgment
If (Index > = 0 )
Updateitem (item );
Else
_ Item. Add (item );
}
}
The application meets one of the following conditions. Class A should be responsible for creating Class B:
(1) A is the aggregation of B.
(2) A is the container of B.
(3) A has data for initializing B.
(4) instance a records B.
(5) A frequently uses B.
When a class has the responsibility to create instances of other classes, these two classes are connected (also called coupling. The connection itself is correct, but we need to go out with some bad links. Inter-class coupling is used to measure the degree of dependency between one class and the other. When two classes are coupled, one class needs another class to work correctly. In program design, the Creator mode is used as the principle. Do not design coupling for applications that do not meet the preceding conditions.
- Low coupling (low coupling)
Low coupling means that our design has the responsibility to reduce (reduce) the connections between classes. Low coupling is very important:
(1) low coupling reduces the impact of a class modification on other classes.
(2) low coupling makes the system easier to maintain.
(3) low coupling makes the class easier to understand, because the class will become simple, professional, and highly cohesive.
Coupling between classes A and B is allowed in the following cases:
(1) A has a B attribute.
(2) A calls the method of object B.
(3) The method of A includes the effect on B. For example, the return value is B type or the parameter value is B type.
(4) A is a subclass of B, or a is an implementation class of B.
Don't talk to strangers. Principle:
Do not connect two objects that do not need to communicate with each other, or do not perform inaction coupling.
Rule two:
(1) If a is connected to B, if it is not appropriate to assign responsibility A to B (in violation of the information expert model), B is assigned to.
(2) the connection between internal classes in the two modules is a big mistake.
For example, in the above example of the Creator model-invoice and invoiceitem, the price (total price) of the whole shipment order needs to be calculated. The business rule is that the shipping person calculates the shipment order, however, because the coupling between the shipment order and the item already exists, the responsibility should be assigned to the shipment order class invoice to reduce inter-class connections. Add the totalprice Method to the invoice class to calculate the total price of the shipment order.
- High Cohesion (High Cohesion)
High cohesion refers to keeping cohesion the highest when assigning responsibilities. It aims to improve the reusability of design and control the complexity of design. The easy-to-understand explanation is that high cohesion means that we need to break down classes so that the decomposed classes have independent responsibilities.
A very low cohesion refers to a class that processes transactions of many different modules separately. If there is a class that processes both data storage and user interface operations or graphic processing, this cross-module design is very low cohesion.
A relatively low cohesion refers to the process of all the transactions of a module separately, but it has too many responsibilities, that is, there are too many attributes and methods in the class. For example, in a data access system, SQL, Oracle, or file access are all completed by one class. To achieve high cohesion, we should break down this class to make it more lightweight.
The purpose of the High Cohesion tone is that the class only processes functions related to the module and works with other classes to complete the task.
The disadvantages of low cohesion are as follows:
(1) It is difficult to understand and maintain. This type may include thousands of lines of code, nearly hundreds of methods or attributes.
(2) It is difficult to reuse classes.
(3) the system becomes fragile and constantly needs to be modified.
When you want to assign new responsibilities, first ask yourself if other functions in the class are related to this responsibility? If not, it is best to assign this responsibility to another class. If the newly added responsibilities are not related to other classes, you can create a new class. For example, there is an ordering class. Now we need to add the responsibility to support data access to excel tables to support different data sources. Should this responsibility be assumed in the order class? Is order details and data access a concept? Of course not, they are not even similar, so we should create a class orderdao to process data access. For example:
The advantages of high cohesion can be reflected in the above example:
(1) high cohesion can represent an abstraction of associated responsibilities, making it easy to reuse classes. The above class design can be extended to the following design: the SAVE () and restore () methods are drawn out by oracledao, and then the sub-class orderdaoexcel can be used to access Excel Data, the SQL database access function is implemented by orderdaosql,
(2) high cohesion makes maintenance easy. Assume that an error occurs when accessing an Excel table. The problem is of course in the orderdaoexcel class.
(3) high cohesion makes the system modular and supports team work. One person can access the Excel file, and the other can access the SQL database.
People usually assign responsibility for receiving and processing system events to the following categories:
(1) categories that fully represent systems, devices, or subsystems.
(2) class that can represent the scenario where a system event occurs.
(3) classes that are involved in real-world applications (such as character role controllers ).
These classes are controller classes. Do not assign these responsibilities to the User Interface Class (such as window, dialog box dialog, page, etc.), because the Controller class is not a user interface class. GRASP has the following consensus:
(1) Receiving and Processing of system events is usually replaced by an advanced class.
(2) A sub-system has many controller classes to process different transactions respectively.
For example, in a banking system, we have a controller class for processing all banking transactions, that is, class transactioncontroller.
When the cashier clicks the button on the user interface to execute the payment bill, the transaction control class transactioncontroller will obtain and process the event. The MVC three-layer architecture emphasizes the application of the control mode. Of course, in some special circumstances, for example, if a user interface generates events on other user interfaces, this user interface also has some control logic, but it is still handled by the Controller class.
If the system only designs one controller class and one view class to process system transactions, it will violate the high cohesion principle and is not a good design.
- Polymorphism (polymorphism)
When the related behaviors are due to different types, you can assign relevant responsibilities to the specified type. Polymorphism allows you to design a component plug-in system. Next we will consider a painting system which will draw various shapes,
First, define an abstract shape class shape. rectangular, circular, and elliptical shapes override the draw method of the Shape class to draw yourself. With this polymorphism mechanism, the painting responsibility is assigned to the specified class. In this way, adding a new shape to the system will not affect other classes. Polymorphism can improve the cohesion program.
This feature of polymorphism honors object-oriented design and has become one of the three main characteristics of object-oriented. Three main characteristics of object-oriented architecture are polymorphism, inheritance, and encapsulation.
- Pure fabrication (purely fictitious)
Achieving high cohesion and low coupling is both the goal of system design and the responsibility of software designers. However, high cohesion conflicts with low coupling because the number of classes in High Cohesion increases, and the links between objects need to be increased to complete tasks in collaboration, which improves coupling. To solve this conflict, we can apply the purely fictitious mode, which adds the features of high cohesion. In the painting example in the above section, if we want to consider painting in different systems, such as Linux and Windows, how should we design it? How to make the classes we designed independent of the system? We should design a High-level class for assigning high cohesion responsibilities,
The above Entity class is a purely fictitious design. No matter which system (Windows or Linux) requires entities to complete painting. It is expressed as a concept in pure fiction. For example, if the system needs to access data, we will design another pure virtual structure, which includes methods for adding and updating data,
As for how to access substantive data, you can use subclass to inherit persistentstorage. The abstract factory mode of gof is an actual embodiment of the pure fictitious mode of grasp.
To avoid direct coupling between objects, you can assign the responsibility of the coordination component or service to the intermediate object, which is called an indirect or intermediate object. As we mentioned earlier, directly coupling two subsystems is wrong and may cause maintenance difficulties. The indirect mode is a solution. In addition, the introduction of intermediate objects can improve the reusability of the two subsystems. For example, in the personnel system, the relationship between positions and employees is allocated to positions, and they are connected. However, potential employees and positions are independent. Therefore, we need to design an intermediate class-assignment to connect employees and positions,
When two subsystems cannot be independently applied, we can use abstract concepts to couple them. abstract classes are used to connect relevant abstract classes. Then, their subclasses can be independently applied without considering this connection.
The gof facade mode is also an example of indirection. Facade avoids the direct connection between two subsystems. The classes of each subsystem are only directly connected to the facade class, so that changes to a sub-system class will not affect the classes of other subsystems. In addition, the gof mediation mode is indirect.
- Protected variations (protected changes)
We have the responsibility to create stable interfaces for expected or unstable points. For example, a game developer is dedicated to developing a universal game engine, which is an outer packaging module. developers can use the same game engine to develop different games. The implementation details of this type of packaging support calls from different customers.
Protected changes can also be understood as the principle of open closed principle (OCP), which means that a software entity should be open to extensions and closed to modifications. In other words, when designing a module, we should make it scalable without being modified.
This advantage is that by extending the existing software system, new behaviors can be provided to meet the new requirements of the software, so that the changed software system has certain adaptability and flexibility. The existing software modules, especially the most important abstract layer modules, cannot be modified, which makes the software system in change stable and continuous.
A common example of protected changes is the gof adapter mode. Games developed using OpenGL interfaces also need to use direct3d. In this way, an OpenGL-to-direct3d adapter must be applied,
Other examples of protected changes include JDBC and ODBC, which are public interfaces for linking databases and allow users to write applications running on database servers of different vendors. The same application uses the same interface to access different databases because it implements a protected mode.