Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of API Design in Java

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article shares with you the content of a sample analysis of API design in Java. The editor thinks it is very practical, so share it with you as a reference and follow the editor to have a look.

Preface

Learn about some API design practices that should be used when designing Java API. These practices are often useful and ensure that API is used correctly in modular environments such as OSGi and Java Platform Module System (JPMS). Some practices are prescriptive while others are prohibitive. Of course, other good API design practices also apply.

The OSGi environment provides a modular runtime that uses the Java classloader concept to enforce type visibility encapsulation. Each module will have its own classloader, which will connect to the classloader of other modules to share the exported package and use the imported package.

Java 9 introduces JPMS, which provides a modular platform to enforce type accessibility encapsulation using access control concepts from the Java language specification. Each module defines which packages will be exported to make them accessible to other modules. By default, modules in the JMPS layer are all in the same class loader.

A package can contain an API. The clients of these API packages have two roles: API consumer and API provider. API consumers use API implemented by API providers.

In the following design practices, we will discuss the common parts of the package. The members and types of packages are not public or protected (that is, private or accessible by default) and cannot be accessed from outside the package, so they are the implementation details of the package.

The Java package must be a cohesive and stable unit

The design of the Java package must ensure that it is a cohesive and stable unit. In modular Java, a package is an entity shared between modules. A module can export a package so that other modules can use the package. Because a package is a unit shared between modules, it must be cohesive, because all types in the package must be related to the specific purpose of the package.

The use of mixed packages, such as java.util, is discouraged because the types in such packages are usually unrelated to each other. Such uncohesive packages can lead to a large number of dependencies because irrelevant parts of the package reference other unrelated packages, and changes to one aspect of the package affect all modules that depend on the package, even though the module may not actually use the modified part of the package.

Because the package is a shared unit, its contents must be well known, and as the package evolves in future releases, the included API can only be changed in a compatible manner. This means that packages cannot support API supersets or subsets; for example, you can think of javax.transaction as a package with unstable content.

The user of the package must be able to understand what types are provided in the package. This also means that the package should be provided by a single entity, such as an jar file, and cannot be split across multiple entities, because the user of the package must be aware of the existence of the entire package.

In addition, the package must evolve in a compatible manner in future releases. Therefore, packages should be versioned, and their version numbers must evolve according to semantic versioning rules.

But recently I realized that the semantic version control advice for changes to the major version of the package is wrong. The evolution of the package must be an increase in functionality. In semantic versioning, this adds minor versions.

When you remove functionality, you will make incompatible changes to the package instead of adding the major version, and you must use a new package name to keep the original package compatible. When making incompatible changes to a package, you should use the new package name instead of changing the major version.

Minimize packet coupling

Types in one package can refer to types in other packages, such as the parameter type and return type of a method, and the type of a field. This packet-to-packet coupling creates so-called usage restrictions on the package. This means that API consumers must use the same package referenced by the API provider so that they can all understand the type referenced.

In general, we want to minimize this packet coupling in order to minimize usage restrictions on the package. This simplifies deployment by simplifying connection resolution in an OSGi environment and minimizing dependency on fan-out.

Interface takes precedence over class

For API, interfaces take precedence over classes. This is a fairly common API design practice and is also important for modular Java. The use of interface not only improves the degree of freedom of implementation, but also supports a variety of implementations.

Interfaces are critical to separating API consumers from API providers. Both the API provider that implements the interface and the API consumer that calls methods on the interface are allowed to use packages that contain the API interface.

In this way, API consumers do not directly rely on API providers. They all rely only on the API package.

In addition to interfaces, abstract classes are sometimes an effective design choice, but interfaces are usually the first choice, especially given that the latest improvements to the interface allow the addition of default methods.

Finally, API usually requires many small concrete classes, such as event types and exception types. This is fine, but these types should generally be immutable and not used by API users to create subclasses.

Avoid static

Static should be avoided in API. Type should not contain static members. Static factories should also be avoided. The creation of the instance should be separate from API. For example, an API consumer should receive an object instance of type API through dependency injection or an object registry such as java.util.ServiceLoader in the OSGi service registry or jPMS.

Avoiding static is also a good practice for creating testable API, because static is not easy to imitate.

Single case

There are sometimes singleton objects in API design. However, singleton objects should not be accessed through static objects such as static getInstance methods or static fields. When you need to use a singleton object, it should be defined by API as a singleton and made available to API consumers through dependency injection or object registry mentioned above.

Avoid classloader assumptions

API usually has an extensibility mechanism in which API consumers can provide class names that must be loaded by the API provider. The API provider must then use Class.forName (possibly a thread context class loader) to load the class. This mechanism assumes class visibility from the API provider (or thread context class loader) to the API consumer.

The API design must avoid classloader assumptions. A major feature of modularization is type encapsulation. One module (such as an API provider) cannot be visible / accessible to the implementation details of another module (such as an API consumer).

The API design must avoid passing class names between API consumers and API providers, and must avoid assumptions related to the class loader hierarchy and type visibility / accessibility.

To provide an extensibility model, the API design should allow API consumers to pass class objects, or preferably instance objects, to the API provider. This can be done through a method in API or an object registry such as the OSGi service registry. See whiteboard mode.

When the java.util.ServiceLoader class is not used in the JPMS module, it is also affected by the classloader assumption because it assumes that all providers are visible to the thread context classloader or the provided classloader.

This assumption is usually not true in a modular environment, but JPMS allows you to declare that a module provides or uses an ServiceLoader managed service through a module declaration.

Do not make persistence assumptions

Many API designs assume that there is only one construction phase in which objects are instantiated and added to the API, but ignore the possible deconstruction phase in dynamic systems.

API design should take into account that objects can be added or deleted. For example, most listeners API allow you to add and remove listeners. However, many API designs only assume that objects can be added and never delete them. For example, many dependency injection systems cannot undo injected objects.

In an OSGi environment, modules can be added and removed, so it is important to be able to take into account such dynamic API design. The OSGi Declarative Services specification defines a dependency injection model for OSGi that supports these dynamic operations, including undoing injected objects.

Clearly define the type roles of API consumers and API providers

As mentioned in the preface, the client of the API package has two roles: the API consumer and the API provider. API consumers use API, while API providers implement API. For interface (and abstract class) types in API, the API design must specify which types are implemented only by API providers and which types can be implemented by API consumers. For example, the listener interface is typically implemented by the API consumer, and the instance is passed to the API provider.

API providers are sensitive to type changes implemented by both API consumers and API providers. The provider must implement any new changes in the API provider type, and must be aware of and may invoke any new changes in the API consumer type.

API consumers can usually ignore (compatible) changes to the API provider type unless they want to invoke new functionality through the changes. However, API consumers are sensitive to changes in API consumer types and may need to be modified to implement new functionality.

For example, in the javax.servlet package, the ServletContext type is implemented by an API provider, such as a servlet container. Adding a new method to ServletContext requires updating all API providers to implement the new method, but API consumers do not need to make changes unless they want to call the new method.

However, the Servlet type is implemented by API consumers, and adding a new method to Servlet requires modification of all API consumers to implement the new method, as well as modification of all API providers to use the new method. Therefore, the ServletContext type has an API provider role and the Servlet type has an API consumer role.

Because there are usually many API consumers and few API providers, API evolution must be performed very carefully when considering changing API consumer types, while API provider type changes are more lenient.

This is because you only need to change a few API providers to support updated API, but you don't want to change many existing API consumers when updating API. API consumers need to make changes only if they want to use the new API.

OSGi Alliance defines document comments, ProviderType, and ConsumerType to mark the type roles in the API package. These comments are included in osgi.annotation jar for your API to use.

Thank you for reading! This is the end of this article on "sample Analysis of API Design in Java". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, you can share it for more people to see!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report