JAVA basics: how to design an appropriate interface-general Linux technology-Linux programming and kernel information. The following is a detailed description. Abstract: when designing system interfaces, we often encounter the following problems:
How many methods should our interface provide?
Should our interface provide "Atomic method" or "Composite Method "?
Should our interfaces encapsulate (or, can we encapsulate) all the details?
The interface design should take into account the user's usage habits, ease of use, and security of use. Based on my programming experience, we will discuss in detail the two considerations of Interface Design: single and compound interfaces.
Interface
Interfaces provide definitions between different systems or between different components of the system. In software, interfaces provide a barrier to separate objects from implementations, extract abstraction from specifics, and separate users from authors.
From the user's point of view, an interface creates and names the usage of a target object. Some constraints (for example, the Type System during compilation, the exception Mechanism during runtime, And the return value) Enable the class author to reflect and enhance the purpose. Affordances refers to the perceived real attributes of things. These attributes can determine the possible methods used by things and provide clues for operations on things.
One of the responsibilities of a class designer is to reduce the gap between constraints and supply in the interface, to match the target and to a certain degree of freedom, and to minimize the possibility of incorrect use of the target object.
Encapsulation
For encapsulation, it is far more simple than data private. In design, encapsulation often involves self-contained ). If a class requires you to know how to call it (e.g. in a thread environment, when a method is called and another method is called, you must explicitly synchronize the object ), therefore, it is better to encapsulate and hide all these classes (e.g. this class is thread-safe. The previous design has a design vulnerability. Many of its limitations are vague and part of the responsibility is pushed to the user, rather than letting the class provider do the work to complete the class design.
Separating the execution of methods in space or time (such as threads, remote method calls, and message queues) can have a profound impact on the correctness and efficiency of the design. The results of such separation cannot be ignored:
Concurrency introduces the overhead of uncertainty and context selection;
Distribution introduces callback overhead, which may increase continuously and cause errors.
These are design issues. modifying them is not as simple as modifying bugs.
If an interface is mainly composed of an access method (set and get methods), each method directs directly to a private domain, it will be poorly encapsulated. Domain access methods in interfaces usually do not provide information: they cannot communicate, simplify, or abstract objects in use, which usually leads to code lengthy and error-prone.
Therefore, we should first consider the first principle of Interface Design:
Command-Query Separation)
Requirement: ensure that a method is neither a Command nor a Query)
Definition:
Query: When a method returns a value to respond to a problem, it has the nature of query;
Command: When a method needs to change the state of an object, it has the nature of a command;
Generally, a method may be a pure Command mode, a pure Query mode, or a mixture of the two. When designing an interface, if possible, try to unify the interface to ensure that the behavior of the method is strict with the command or query, so that the query method does not change the object status, without side effects, methods that change the state of an object cannot return values. That is to say, if we want to ask a question, it should not affect its answer. The actual application depends on the actual situation. The clarity of semantics and the simplicity of use need to be weighed.
For example, in java. util. Iterator, hasNext can be considered as a query. remove is a command. next combines the command and query:
Public interface Iterator {boolean hasNext (); Object next (); void remove ();}
Here, if you do not forward the current value of an Iterator object to the next one, you cannot query an Iterator object. If a composite method next is not provided, we need to define a series of command methods, such as initialization, continuation, access, and forward (advance ), although they clearly define each action, the customer code is too complicated:
For (initialization; continuation condition; advance) {... access for use ...}
The Command and Query functions are combined into a method to facilitate the customer's use, but the clarity is reduced, assertion-based program design may be difficult and a variable is required to save the query results:
Iterator iterator = collection. iterator (); while (iterator. hasNext ();) {Object current = iterator. next ();... use current ...}
Next, we will consider the second principle of Interface Design:
Combined Method)
The combination method is often used in online and distributed environments to ensure correctness and improve efficiency.
Some interfaces provide a large number of methods. At first, these methods seem to be minimized and highly correlated. However, some interfaces appear too primitive in the Process of use, and they are too simple to force class users to use more work to implement common tasks, the order and dependency between methods are strong (I .e., temporary coupling ). This leads to code duplication, which is very troublesome and error-prone.
Some methods that require successful execution at the same time may cause problems in the case of multithreading, exceptions, and distribution. If two actions need to be executed at the same time, they are described by two independent methods, and must be executed completely successfully. Otherwise, all actions will be rolled back.
The introduction of threads greatly increases this uncertainty. A series of methods call a mutable object at the same time. If this object is shared among threads, even if we assume that the separate method is thread-safe, the results cannot be expected. Let's take a look at the interface for Event Source, which allows handling and Event Query:
Cross-call between threads may cause unexpected results. Assuming that the source domain references an object shared by a thread, the object may be installed with a new handle by another thread between 1 and 2:
Class EventSourceExample {public void example (Event event, Handler newHandler) {oldHandler = eventSource. getHandler (event); // 1 // The object is probably installed with a new handle eventSource by another thread. installHandler (event, newHandler); // 2} private EventSource eventSource; private Handler oldHandler ;}
In order to solve the problem, it is also necessary for the class user rather than the Class Designer:
We assume that the target object eventSource is remote, and the execution time of each method body is short compared with the communication latency. In this example, the eventSource method is called twice and may be repeated multiple times in other instances. Therefore, the overhead is at least twice.
There is also a problem with the use of external synchronized synchronization blocks. The reason why synchronized blocks fail to be used is mainly because we use proxy objects to complete the work. Therefore, the caller's synchronized block synchronizes the proxy object instead of the final target object, it is impossible for callers to make too many guarantees on their behaviors.
The Combined Method must be executed simultaneously in the distributed environment or in the thread environment. It reflects users' direct applications, and the recovery policies and some clumsy methods are encapsulated in the Combined Method. It simplifies the interface and reduces the unnecessary burden in the interface. The effect of Combined Method is to support a transaction processing style design.
It is generally reasonable to provide a separate Query method in a combined Command-Query. It is not common to provide the Command Method for separation, because the Combined Method can do this, as long as the caller simply ignores the returned results. If an overhead is incurred when a result is returned, a separate Command method may be provided.
Return to the previous example. If the installHandler method returns the last installed handle, the design becomes simpler and more independent:
The Customer Code is as follows:
Class EventSourceExample {public void example (Event event, Handler newHandler) {oldHandler = eventSource. installHandler (event, newHandler);} private EventSource eventSource; private Handler oldHandler ;}
In this way, the caller is provided with a safer interface, and they no longer need to solve the thread problem. This reduces the risk and amount of code, and gives all the responsibilities of the class design to the class designer instead of the user. Even if a proxy object appears, the correctness is not affected.
A Combined Method can be a set of many queries, a set of many commands, or both. In this way, it may supplement the Command and Query methods, or conflict with them. When a conflict occurs, selecting Combined Method first produces a different correctness and applicability.
In another example, we will consider obtaining resources. Assume that the method acquire is blocked before the resource is available in the following interface:
The following code is recommended in a thread system:
Class ResourceExample {public void example () {boolean acquired = false; synchronized (resource) {if (! Resource. isAcquired () resource. acquire (); elseacquired = true;} if (! Acquired)...} private Resource ;}
However, even if we give up readability and usability, this design is not a Command-Query separation design. If a proxy is introduced, it will fail:
Class ActualResource implements Resource {...} class ResourceProxy implements Resource {...}
If you can do the work either through ActualResource or ResourceProxy, And neither ActualResource nor ResourceProxy processes synchronization, the synchronized block may fail. Because, since we can complete the work through the proxy object ResourceProxy, the caller's synchronized block synchronizes the proxy object ResourceProxy instead of the final target object ActualResource.
A Combined Method solves this problem and makes concurrency and indirect more transparent.
Interface Resource {
Boolean tryAcquire ();
}
The following code is clear, simple, and correct:
Class ResourceExample {public void example () {if (! Resource. tryAcquire ()...} private Resource resource ;}
One result of Combined Method is that it makes some testing and assertion-based programming very clumsy. However, it is suitable for solving thread and distribution issues.
In actual application, the interface should be single or composite, depending on the specific situation.
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.