How to Address Crosscutting Concerns in Aspect Oriented Software Development
Component-based development is a widely used approach to build complex systems. Basically, you allocate requirements to components of some kind—classes, packages, services, and so forth. Although many requirements can be effectively localized to individual components, you find many requirements that cannot be localized to an individual component and that sometimes even impact many components. In aspect-speak, these requirements cut across components and are called crosscutting concerns. The inability to keep such concerns separate during design and implementation makes a system difficult to understand and maintain. It inhibits parallel development and makes a system difficult to extend and results in many of the problems that plague so many projects today. A successful solution to this problem involves two things: an engineering technique to separate such concerns from requirements all the way to code and a composition mechanism to merge the design and implementation for each concern to result in the desired system. With aspect orientation under the guidance of an appropriate methodology, you do have such a solution today.
1.1 The Use of Components Today
Software systems are important to businesses today. Most, if not all, businesses today cannot run without the help of software to conduct business operations. As we all know, software systems are complex and when we design such complex systems our limited minds cannot possibly consider everything and solve everything at once.
Our natural inclination is to break the problem into smaller parts and solve them one by one. This is why we have components. Each component plays its specific role and has specific responsibilities and purposes. We assemble components of various kinds to form the complete system. This is basically how we develop any kind of product: electronic devices, cars, and more.
In a generic sense, components are elements that conform to well-defined interfaces, and if you invoke them through their interfaces, they produce some well-defined responses. For example, a computer chip is a component. It has pins through which you can send electric signals. Upon receiving the signal, the chip performs some actions and possibly returns some response through some other pins. Your video projector is also a component. If you plug in a video cable from your laptop to the projector, you can make images appear on the wall.
A component encapsulates its contents. Its internals are all hidden from you. As a user of the component, you do not need to know how it really works on the inside. All you need to know is that you send the correct signals to it through its interfaces in some acceptable sequence and you get your desired response. This characteristic of components is very attractive because as long as the interfaces do not change, you can replace them with other components that conform to those same interfaces. This substitutability is extremely useful if you want to extend the system with some new capabilities—all you need to do is replace an existing component with a better one that conforms to the same interface. Even if you have to change an interface, you may delimit the changes to a few components. It allows you to gracefully grow a complex system.
1.1.1 Building a System with Components
The usual approach to building systems in terms of components is as follows: You begin by first understanding what the system is supposed to do: What are the stakeholder concerns? What are the requirements? Next, you explore and identify the parts (i.e., components) that will constitute the system. You then map the world of requirements to the world of components. This is an M-to-N mapping and, normally, M is much larger than N. For example, you might have 1,000 requirements and maybe 50 components. The common approach to mapping is as follows: You identify a set of candidate components and check that each requirement will be implemented with these components. In this process, you may learn more about the requirements and, provided that the requirements are not too critical, change them so that they are easier to implement. Alternately, you might modify components to improve the fit. Once the required set of components is found, they are connected to form the desired system.
Figure 1-1 shows the components for a Hotel Management System. We use this system as an example here and throughout the rest of the book. Briefly, this system provides the functionalities to Reserve Room, Check In, and Check Out to be used by both customers and hotel staff.
Figure 1-1 shows components of various kinds. The Customer Screen and Staff Screen components deal with presenting information to the users and accepting and validating input from them. The Reserve Room, Check In, and Check Out components encapsulate the business and control logic for the respective functionalities. The reservation and the room components maintain information in a data store. This separation of roles and responsibilities across components is essential to a system that is resilient—one that will not easily break when changes are introduced.
1.1.2 Benefits of Components
Components are useful and important because they represent the static structure of a system such as that depicted in Figure 1-1. They are essential to understanding, designing, implementing, distributing, testing, and configuring the system. They are the most important asset for reuse in practice. Components contain things that change together. They keep concerns about a kind of object or an abstraction of a real-world phenomena separate.
For instance, a component (e.g., the Room and Reservation components in Figure 1-1) may encapsulate the manipulation of data structures capturing room and reservation information. If you change the data structure, you change the operations that touch these data. There are also components encapsulating the specifics of user interfaces. If you want to change the look and feel of the system, you simply change the screen components. Thus, you see that components make a system resilient to changes as you add new requirements to the system.
You can also meet customers' new demands by configuring the system using the components. New features or use-cases are usually provided by adding a new component and changing some already existing components.
In recent years, component frameworks such as J2EE and .Net have evolved and gained widespread popularity. Basically, all new software being developed is componentized.