Book Image

Software Architecture with C++

By : Adrian Ostrowski, Piotr Gaczkowski
Book Image

Software Architecture with C++

By: Adrian Ostrowski, Piotr Gaczkowski

Overview of this book

Software architecture refers to the high-level design of complex applications. It is evolving just like the languages we use, but there are architectural concepts and patterns that you can learn to write high-performance apps in a high-level language without sacrificing readability and maintainability. If you're working with modern C++, this practical guide will help you put your knowledge to work and design distributed, large-scale apps. You'll start by getting up to speed with architectural concepts, including established patterns and rising trends, then move on to understanding what software architecture actually is and start exploring its components. Next, you'll discover the design concepts involved in application architecture and the patterns in software development, before going on to learn how to build, package, integrate, and deploy your components. In the concluding chapters, you'll explore different architectural qualities, such as maintainability, reusability, testability, performance, scalability, and security. Finally, you will get an overview of distributed systems, such as service-oriented architecture, microservices, and cloud-native, and understand how to apply them in application development. By the end of this book, you'll be able to build distributed services using modern C++ and associated tools to deliver solutions as per your clients' requirements.
Table of Contents (24 chapters)
1
Section 1: Concepts and Components of Software Architecture
5
Section 2: The Design and Development of C++ Software
6
Architectural and System Design
10
Section 3: Architectural Quality Attributes
15
Section 4: Cloud-Native Design Principles
21
About Packt

Cohesion

Cohesion is a measure of how strongly a software unit's elements are related. In a highly cohesive system, the functionality offered by components in the same module is strongly related. It feels like such components just belong together.

On a class level, the more fields a method manipulates, the more cohesive it is to the class. This means that the most commonly spotted low-cohesion data types are those big monolithic ones. When there's too much going on in a class, it most probably is not cohesive and breaks the SRP, too. This makes such classes hard to maintain and bug-prone.

Smaller classes can be incohesive as well. Consider the following example. It may seem trivial, but posting real-life scenarios, often hundreds if not thousands of lines long, would be impractical:

class CachingProcessor {
public:
Result process(WorkItem work);
Results processBatch(WorkBatch batch);
void addListener(const Listener &listener);
void removeListener(const Listener &listener);

private:
void addToCache(const WorkItem &work, const Result &result);
void findInCache(const WorkItem &work);
void limitCacheSize(std::size_t size);
void notifyListeners(const Result &result);
// ...
};

We can see that our processor actually does three types of work: the actual work, the caching of the results, and managing listeners. A common way to increase cohesion in such scenarios is to extract a class or even multiple ones:

class WorkResultsCache {
public:
void addToCache(const WorkItem &work, const Result &result);
void findInCache(const WorkItem &work);
void limitCacheSize(std::size_t size);
private:
// ...
};

class ResultNotifier {
public:
void addListener(const Listener &listener);
void removeListener(const Listener &listener);
void notify(const Result &result);
private:
// ...
};

class CachingProcessor {
public:
explicit CachingProcessor(ResultNotifier &notifier);
Result process(WorkItem work);
Results processBatch(WorkBatch batch);
private:
WorkResultsCache cache_;
ResultNotifier notifier_;
// ...
};

Now each part is done by a separate, cohesive entity. Reusing them is now possible without much hassle. Even making them a template class should require little work. Last but not least, testing such classes should be easier as well.

Putting this on a component or system level is straightforward – each component, service, and system you design should be concise and focus on doing one thing and doing it right:

Figure 1.2 – Coupling versus cohesion

Low cohesion and high coupling are usually associated with software that's difficult to test, reuse, maintain, or even understand, so it lacks many of the quality attributes usually desired to have in software.

Those terms often go together because often one trait influences the other, regardless of whether the unit we talk about is a function, class, library, service, or even a whole system. To give an example, usually, monoliths are highly coupled and low cohesive, while distributed services tend to be at the other end of the spectrum.

This concludes our introductory chapter. Let's now summarize what we've learned.