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

How to understand Java API design

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

Share

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

This article mainly introduces "how to understand Java API design". In daily operation, I believe many people have doubts about how to understand Java API design. Xiaobian consulted various materials and sorted out simple and easy operation methods. I hope to help you answer the doubts of "how to understand Java API design"! Next, please follow the small series to learn together!

preface

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

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

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 and made accessible to other modules. By default, modules in the JMPS layer are all located in the same class loader.

A package can contain an API. Clients of these API packages have two roles: API consumers and API providers. API consumers consume APIs implemented by API providers.

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

Java package must be a cohesive, stable unit

Java packages must be designed to be cohesive and stable units. In modular Java, packages are entities shared between modules. A module can export a package so that other modules can use it. 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 promiscuous packages, such as java.util, is discouraged because the types in such packages are usually unrelated to each other. Such non-cohesive packages can lead to a large number of dependencies because unrelated 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.

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

Users of packages must be able to understand which types are provided in the package. This also means that a package should be provided by a single entity (e.g. a jar file) and should not be split across multiple entities, since users of the package must be aware of the existence of the entire package.

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

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

When you remove features, you make incompatible changes to the package, rather than adding major versions, and you must change to a new package name to keep the original package compatible. When you make 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 reference types in other packages, such as the parameter types and return types of a method, and the types of a field. This packet coupling creates so-called usage restrictions on the packet. This means that API consumers must use the same package referenced by the API provider so that they all know the type being referenced.

In general, we want to minimize this packet coupling in order to minimize usage restrictions on packets. This simplifies connection resolution in OSGi environments and minimizes dependency fanout, simplifying deployment.

Interface takes precedence over class

For APIs, interfaces take precedence over classes. This is a fairly common API design practice and is also important for modularizing Java. The use of interfaces increases the freedom of implementation and supports multiple implementations.

Interfaces are critical to separating API consumers from API providers. Packages containing API interfaces are allowed, both by API providers implementing the interfaces and API consumers invoking methods on the interfaces.

In this way, API consumers are not directly dependent on API providers. They all rely solely on API packages.

In addition to interfaces, abstract classes are sometimes a valid design choice, but interfaces are often preferred, especially considering recent improvements to interfaces that allow default methods to be added.

Finally, APIs often require 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 consumers to create subclasses.

avoid static

Static should be avoided in the API. Type should not contain static members. Static factories should also be avoided. Instance creation should be separate from API. For example, API consumers should receive object instances of API types through dependency injection or object registries such as java.util.ServiceLoader in the OSGi service registry or jPMS.

Avoiding static is also a good practice for creating testable APIs, as static is not easy to emulate.

Singleton

Singleton objects sometimes exist in API designs. However, singleton objects should not be accessed through static objects such as static getInstance methods or static fields. When a singleton object is required, it should be defined by the API as a singleton and made available to API consumers via dependency injection or the object registry mentioned above.

Avoid class loader assumptions

APIs often have extensibility mechanisms in which API consumers can provide class names that API providers must load. The API provider must then load the class using Class.forName (possibly using a thread context class loader). This mechanism assumes class visibility from the API provider (or thread context class loader) to the API consumer.

API design must avoid class loader assumptions. A key feature of modularity is type encapsulation. One module (e.g. API provider) cannot have visibility/accessibility to the implementation details of another module (e.g. API consumer).

API design must avoid passing class names between API consumers and API providers, and must avoid assumptions about 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 API providers. This can be done through a method in the API or an object registry such as the OSGi service registry. See Whiteboard mode.

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

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

No persistence assumptions

Many API designs assume only one construction phase, where objects are instantiated and added to the API, but ignore the deconstruction phase that can occur in dynamic systems.

API design should take into account that objects can be added or deleted. For example, most listener APIs allow you to add and remove listeners. However, many API designs assume only 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's important to be able to accommodate this dynamic API design. The OSGi Declarative Services specification defines a dependency injection model for OSGi that supports these dynamic operations, including undo injection objects.

Clearly define the type roles of API consumers and API providers

As mentioned in the introduction, clients of API packages have two roles: API consumers and API providers. API consumers consume APIs and API providers implement APIs. For interface (and abstract class) types in an API, the API design must clearly specify which types are implemented only by API providers and which types can be implemented by API consumers. For example, listener interfaces are typically implemented by API consumers, while instances are passed to API providers.

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 likely to invoke any new changes in the API consumer type.

API consumers can generally ignore (compatible) changes to API provider types unless they want the changes to invoke new functionality. But 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 a ServletContext requires updating all API providers to implement the new method, but API consumers do not need to make changes unless they want to invoke the new method.

However, Servlet types are implemented by API consumers, and adding a new method to a Servlet requires modifying all API consumers to implement the new method and 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.

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

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

The OSGi Alliance defines document comments, ProviderTypes, and ConsumerTypes to mark type roles in API packages. These comments are included in the osgi.annotation jar for use by your API.

At this point, the study of "how to understand Java API design" is over, hoping to solve everyone's doubts. Theory and practice can better match to help everyone learn, go and try it! If you want to continue learning more relevant knowledge, please continue to pay attention to the website, Xiaobian will continue to strive to bring more practical articles for everyone!

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