当前位置:网站首页>Some basic design knowledge

Some basic design knowledge

2022-06-13 00:23:00 carl-zhao

Reprinted address : Some basic design knowledge

Recently, I told some common sense of design to the new members of the team , It may also help other newcomers ,
Put a few items that come to mind for the time being , Let's write it down here .

1、API And SPI Separate

A framework or component usually has two types of customers , One is the user , One is the extender .
API(Application Programming Interface) It is for users , and SPI(Service Provide Interface) It is for the extender . In design , Try to isolate them , And don't mix it up , in other words , The user cannot see the implementation written by the extender .

such as :
1. One Web frame , It has one API The interface is called Action, There's a execute() Method , It is for users to write business logic . then ,Web The frame has a SPI The interface controls the output mode for the extender .
2. velocity Template output is still in json Output, etc , If this Web The framework uses an all - inheritance Action Of VelocityAction And a JsonAction As an extension , Use velocity The output of the template inherits VelocityAction, Use json Output inheritance JsonAction, This is it. API and SPI There is no negative example of separation .

SPI Interfaces are mixed API Interface , It's a reasonable way , There's a separate one Renderer Interface , Yes VelocityRenderer and JsonRenderer Realization , Web The framework will Action The output of is forwarded to Renderer Interface to render output .

Anyway, the example :
 Picture description here

The right example :
 Picture description here

2、 Service domain / Entity domain / Session domain separation

Any frame or component , There will always be a core domain model , such as :

Entity domain : image Spring Of Bean,Struts Of Action,Dubbo Of Service,Napoli Of Queue wait . This core domain model and its components are called entity domains , It represents the goal we want to operate on , Entity domains are usually thread safe , Whether through invariant classes , sync , Or copy .

Service domain : That is, the behavior domain , It is the function set of the component , It is also responsible for the lifecycle management of entity domain and session domain . such as Spring Of ApplicationContext,Dubbo Of ServiceManager etc. , Service domain objects are usually heavy , And it's thread safe , And serve all calls with a single instance .

Session domain : It is an interactive process , The important concept in conversation is context , What is context ? Let's say :“ See you in the old place ”, there “ Old Place ” Context information , Why do you say “ Old Place ” The other person will know , Because we defined “ Old Place ” Specific content of , So , Context usually holds the state variables in the interaction process , The session object is usually light , Each request recreates the instance , Destroy after request .

In short :

Leave the meta information to the entity domain , The temporary state in a request is held by the session domain , The service domain runs through the whole process .

 Instance of a two Kind of example Son  Example 2

3、 Set the interception interface on the important process


  1. If you want to write a remote call framework , The remote calling process should have a unified interception interface ;
  2. If you want to write one ORM frame , At least SQL Implementation process of ,Mapping The process should have an interception interface ;
  3. If you want to write one Web frame , The request execution process should have an interception interface ;

No common framework can Cover Live with all the needs , Allow external behavior , Is the basic extension of the framework .

  1. If someone wants to make a remote call , Verify the command board , Verify the black and white list , Count the logs ;
  2. If someone wants to be in SQL Add pagination wrapping before execution , Do data permission control , Statistics SQL execution time ;
  3. If someone wants to check the role before the request is executed , The input / output stream under the packaging , Count the number of requests ;

wait , You can do it yourself , Without intruding into the frame . Interception interface , Usually, the process itself is encapsulated by an object , To the interceptor chain .

such as : The remote calling main procedure is invoke(), The interceptor interface is usually invoke(Invocation),Invocation Object encapsulates the context in which the procedure is to be executed , also Invocation There's one in it invoke() Method , The interceptor decides when to execute . meanwhile ,Invocation It also represents the interceptor behavior itself , So the last interceptor Invocation In fact, it is the process of packaging the next interceptor , Until the last interceptor Invocation It is the final of packaging invoke() The process , Empathy ,SQL The main process is execute(), The interceptor interface is usually execute(Execution), The principle is the same , Of course , The implementation method can be arbitrary , The above is just an example .
 Picture description here

4、 Important state changes send events and leave listening interfaces

Here is the difference between an event and the interceptor above :

Interceptor : Is the process of intervention , It's part of the process , It's based on process behavior .
event : Is based on status data , The same state of any behavior change , The events should be consistent , Events are usually notified afterwards , It's a Callback Interface , Method names are usually past tense , such as onChanged().

For example, the remote call framework , An event should be sent when the network is disconnected or connected , When an error occurs, you can also consider sending an event , In this way, it is possible for peripheral applications to observe changes within the framework , Adapt accordingly .

 Picture description here

5、 The responsibilities of the extension interface shall be as single as possible , It has composability

such as , The protocol of the remote call framework can be replaced , If only one general extension interface is provided , Of course, switching protocols can be achieved , But protocol support can be subdivided into low-level communication , serialize , Dynamic proxy, etc , If the interface is disassembled , Orthogonal decomposition , It will be easier for the extender to reuse the existing logic , Instead, it just replaces some part of the implementation strategy , Of course, the granularity of this decomposition needs to be well grasped .

6、 Micronucleus plug-in , Treat third parties equally


A good framework for development , We all follow the concept of micronucleus


Eclipse My micronucleus is OSGi, Spring My micronucleus is BeanFactory,Maven My micronucleus is Plexus.

Usually the core should not be functional , It is a lifecycle and integration container , In this way, all functions can interact and expand in the same way , And any function can be replaced , If you can't do micronucleus , At least treat third parties equally , That is, the functions that the original author can realize , The extender should be able to do it all by extension , The original author should regard himself as an extender , Only in this way can the sustainability and stability of the framework be ensured .

7、 Do not control the lifecycle of external objects

Like the above Action Use interfaces and Renderer Extension interface , If the framework allows users or extenders to Action or Renderer The class name or class meta information of the implementation class is reported . And then internally through reflection newInstance() Create an instance , So the framework controls Action or Renderer Implement the life cycle of the class , Action or Renderer Life, old age and death of , All the frames have been made by themselves , External expansion or integration can do nothing .

A good idea is to let users or extenders put Action or Renderer An instance of the implementation class is reported , The framework only uses these instances , How these objects are created , How to destroy , It has nothing to do with the framework , At most, the framework provides tools to assist management , Not absolute control .

8、 Configurable must be programmable , And keep friendly CoC Appointment

Because there are many uncertain factors in the use environment , Frameworks always have some configuration , It usually comes to classpath Directly scan the configuration of a specified name , Or you can specify the configuration path at startup , As a general framework , What can be done with the configuration file must be done by programming , Otherwise, when users need to integrate your framework with another framework, it will bring a lot of unnecessary trouble .

in addition , Try to make a standard agreement , If the user does something according to a certain agreement , This configuration item is not required . such as : Configure template location , You can make an appointment , If you put it in templates There is no need to configure under the directory , If you want to change the directory , Just configure it .

9、 Distinguish between commands and queries , Define pre and post conditions

This is part of contractual design , Try to follow the method that has a return value is the query method ,void The way to return is the command , Query methods are usually idempotent , No side effects , That is not to change any state , transfer n The results are the same . such as get A property value , Or query a database record .

Command is something that has side effects , That is, the status will be modified , such as set A certain value , or update A database record , If your method changes the State , Then I did a query and returned , If possible , Split it into two methods of write read separation .

such as :
1. User deleteUser(id), Delete the user and return the deleted user , Consider changing to getUser() and void1 Of deleteUser().
2. in addition , Each method tries to pre assert the legitimacy of the incoming parameters , The validity of the returned result of the post assertion , And documented .

10、 Incremental expansion , Instead of extending the original core concepts

There are more and more products on our platform , The product has more and more functions , The products of the platform are designed to adapt to various BU And the needs of departments and product lines . It is bound to bring together many irrelevant functions , Customers can selectively use , To accommodate more requirements , Each product , Every frame , Are constantly expanding , And we often choose some extension methods , That is to extend the old and new functions into a common implementation .

What I want to discuss is , In some cases, the incremental expansion method can also be considered , That is, keep the simplicity of the original function , New functions are implemented independently . I have been working on the development of distributed service framework recently , Just make fun of the problems in our project .

such as : Remote call framework , There must be serialization , The function is very simple , Is to turn the flow into an object , Object to stream , But it may be used in some places osgi, When serializing like this ,IO Where ClassLoader May be related to the business party ClassLoader It's isolated , You need to convert the stream to byte[] Array , And then passed to the business party ClassLoader serialize .

In order to adapt osgi demand , Put the original non osgi And osgi The scene of is expanded , such , Whether or not osgi Environmental Science , Will be transferred into byte[] Array , Copy once . However , Most scenes don't use osgi, But for osgi Paid a price , If the incremental expansion mode is adopted , Not osgi The code is intact , Add one osgi The implementation of the , Use osgi When , Directly dependent on osgi It can be realized .

Another example : In the beginning , Remote services are based on interface methods , For transparent calls , such , The extension interface is ,invoke(Method method, Object[] args), later , There is a need for no interface calls , Even if there is no interface method, it can be called , And will POJO Objects are converted to Map Express , because Method Objects are not directly new Coming out , We unconsciously chose an extensible extension , Change the extension interface to invoke(String methodName, String[] parameterTypes, String returnTypes, Object[] args), This results in no matter whether or not there is no interface call , All of them have to be cleaned up parameterTypes from Class[] Turn into String[].

If you choose incremental expansion , Keep the original interface unchanged , Add one more GeneralService Interface , There is a general purpose invoke() Method , The same way as other interfaces in normal business , There is no need to change the extension interface , It's just GeneralServiceImpl Of invoke() The implementation will forward the received call to the target interface , In this way, the new functions can be added to the old functions , And keep the original structure simple .

Another example is : Stateless message sending , It's simple , Just serialize an object and send it , Later, there was a need to send synchronous messages , Need one Request/Response Pairing , Adopt extensible expansion , Naturally think of , A stateless message is actually a no Response Of Request, So in Request Riga boolean state , Indicates whether to return Response, If there is another session message sending requirement , Then add another Session Interaction . And found that , It turns out that synchronous message sending is a special case of session messages , All the scenes are passed on Session, Unwanted Session Just ignore the place . If you use incremental expansion , Stateless messages are sent intact .

Synchronous message sending , Add a to the stateless message Request/Response Handle ,
Session message sending , Add one SessionRequest/SessionResponse Handle .

 Picture description here

 Picture description here

原网站

版权声明
本文为[carl-zhao]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202280601069798.html