• Tidak ada hasil yang ditemukan

Software architecture with C++

N/A
N/A
Moses Kota

Academic year: 2023

Membagikan "Software architecture with C++"

Copied!
522
0
0

Teks penuh

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information in this book is sold without warranty, either expressed or implied.

Architectural Quality Attributes

Continuous Integration and Continuous Deployment 269

Security in Code and Deployment 296

Cloud-Native Design Principles

Service-Oriented Architecture 350

We'll show you how to design and build apps that are robust, scalable, and perform well. You will learn how to apply established design patterns at the single application level and explore how to make your applications robust, secure, performant, and maintainable.

Who this book is for

You will then build higher-level services that connect multiple individual applications using patterns such as service-oriented architecture, microservices, containers, and serverless technology. By the end of this book, you will be able to build distributed services using modern C++.

What this book covers

Complete with step-by-step explanations of essential concepts, practical examples and self-assessment questions, you will begin by understanding the importance of architecture, looking at a case study of an actual application. Chapter 10, Security in Code and Implementation, is where you learn how to make sure your systems are hard to break.

To get the most out of this book

If you are using the digital version of this book, we recommend that you type the code yourself or access the code through the GitHub repository (link available in the next section). Doing this will avoid potential errors related to copying and pasting code.

Download the example code files

If you're on Windows, Windows Subsystem for Linux 2 is a great way to have a Linux development environment with Nix.

Download the color images

Conventions used

Get in touch

Reviews

Concepts and 1

This section introduces you to the basics of software architecture and demonstrates effective approaches to its design and documentation. The purpose of this introductory chapter is to show the role software architecture plays in software development.

Technical requirements

Understanding software architecture

Different ways to look at architecture

Learning the importance of proper architecture

Software decay

Accidental architecture

Exploring the fundamentals of good architecture

The Importance of Software Architecture and Principles of Great Design Chapter 1 So, in order for your product to meet business requirements and features like performance, durability, scalability, or others, you need to take care of its architecture, and it's best if you do it as early as possible in the process.

Architecture context

Stakeholders

Business and technical environments

Other systems that we need to integrate with are also a vital part of the technical environment. The technical expertise of the available software engineers is also important here: the technology decisions an architect makes can affect the staffing of the project, and the ratio of junior to senior developers can affect how a project should be managed.

Developing architecture using Agile principles

Importance of Software Architecture and Principles of Great Design Chapter 1 By technical environment we mean the technologies already used in a company or those that are required for any reason to be part of the solution. Armed with all this knowledge, let's now discuss a somewhat controversial topic that you are most likely to encounter in your daily work as an architect.

Domain-driven design

Ideally, each can be mapped to its own limited context—a part of your system with its own vocabulary. Like its context, each module has clear responsibilities, its own database schema, and its own code base.

The philosophy of C++

The Importance of Software Architecture and Principles of Great Design Chapter 1 Each of these is a different subdomain of your eCommerce domain. The Importance of Software Architecture and Principles of Great Design Chapter 1 auto timeout = 1s; // or std::chrono::seconds(1);.

Following the SOLID and DRY principles

Single responsibility principle

Open-closed principle

Importance of Software Architecture and Principles of Great Design Chapter 1 A related topic is the principle of least knowledge.

Liskov substitution principle

Speaking of polymorphism, let's move on to the next principle, because it's all about using it correctly. Importance of Software Architecture and Principles of Great Design Chapter 1 While we're on the subject of interfaces, let's move on to the next item, which is also related.

Interface segregation principle

Now we have a problem with the Blender class as it does not support this new interface – there is no proper way to implement it.

Dependency inversion principle

The Importance of Software Architecture and Principles of Great Design Chapter 1 Now we no longer rely on the interface, so no virtual shipping will be done. This approach is guaranteed not to allocate separate memory for each developer or use a virtual table.

The DRY rule

Coupling and cohesion

Coupling

It looks like instead of just adding the MiddlewareDeveloper class, we needed to modify the public interface of the Project class. No changes were required to the Project class, so now the classes are loosely coupled.

Cohesion

This means that they are tightly coupled and that this implementation of the Project class actually breaks the OCP. Low cohesion and high coupling are usually associated with software that is difficult to test, reuse, maintain or even understand, so it lacks many of the quality characteristics usually desired in software.

Figure 1.2 – Coupling versus cohesion
Figure 1.2 – Coupling versus cohesion

Summary

Questions

Further reading

We will then begin to look at architectural styles from different perspectives by describing event-based systems, layered systems, and finally modular designs. The code from this chapter can be found on the following GitHub page: https://github.

Deciding between stateful and stateless approaches

If you choose the class approach, should you pass the advisor as a constructor parameter or method argument. The second time our calculations are more complex, the lessons will probably begin.

Stateless and stateful services

This assumes that the data stored on the server does not change, but data does not necessarily equal status. The client-side client will likely store an authentication cookie, while the server will likely store some user data in a database.

Understanding monoliths — why they should be avoided, and recognizing exceptions

If you are a fan of using booming technologies in your project, a monolithic style does not bring good news either. If you are interested in measurements, see the paper listed in the Further Reading section of this chapter.

Understanding services and microservices

If you care about performance, a monolith can sometimes help you squeeze more out of your application in terms of latency or throughput compared to microservices. Communication between processes will always incur some overhead that monolithic applications don't have to pay.

Microservices

Benefits and disadvantages of microservices

Architectural Styles Chapter 2 Although this manifesto does not bind you to a specific technology stack, implementation, or type of services, the two most common types of services are SOAP and REST.

Characteristics of microservices

Architectural Styles Chapter 2 Services must have smart endpoints and use repository pipelines, not the other way around. Additionally, here are some other characteristics shared by many (but not all).

Microservices and other architectural styles

It's tricky to design services properly, especially since some of the complexity that used to be in the code of a larger module can now be present as complex communication schemes between services, where it's harder to manage - the so-called Spaghetti Integration. This means that the experience and skills of the architect play a more important role than with traditional services or a.

Scaling microservices

Transitioning to microservices

Exploring event-based architecture

Common event-based topologies

Examples of event-based systems include stream processors and data integrations, as well as systems aiming for low latency or high scalability. Now that you know the two common topologies used in event-based systems, let's learn about a powerful architectural pattern using events at its core.

Event sourcing

On the other hand, a broker is a lightweight component that contains all queues and does not direct event processing. It can require recipients to subscribe to certain types of events and then simply forward all those that are of interest to them.

Figure 2.3 – Event sourcing architecture. Providing a unified view of the application state can allow for consuming it and creating periodic snapshots for faster recovery
Figure 2.3 – Event sourcing architecture. Providing a unified view of the application state can allow for consuming it and creating periodic snapshots for faster recovery

Understanding layered architecture

For example, you can have a fast-changing layer to collaborate with your business partners, with content that changes frequently, and other business. Now let's discuss another example of a layered architecture often used with microservices - backends for frontends.

Figure 2.4 – An example of a 3-tiered architecture using a textual interface in the presentation layer
Figure 2.4 – An example of a 3-tiered architecture using a textual interface in the presentation layer

Backends for Frontends

There's also a notion that less stable components should rely on more stable components, so it's easy to see that you can have two layers here with the client facing depending on the capabilities of the business. If you're building mobile apps for iOS and Android, for example, you might consider reusing the same backend for them, and having separate ones for web and/or desktop apps.

Figure 2.5 – The Backends for Frontends pattern
Figure 2.5 – The Backends for Frontends pattern

Learning module-based architecture

In the next chapter, you'll learn how to know which of these features are important in a given system. By the end of this chapter, you will have learned how to recognize and categorize both types of requirements, and how to create documentation that clearly describes them.

Technical requirements documentation from sources, you must have

Understanding the types of requirements

Functional requirements

Functional and Non-Functional Requirements Chapter 3 The first of these requirements tells us that we will need to have an order and product tracking component with search capabilities. This means that we could look at an Architecturally Significant Requirement (ASR) which could affect our architecture.

Nonfunctional requirements

Depending on what exactly we want the UI to look like and what scale our app needs to be, we can just add a simple page to our app, or it could require features like Lucene or Elasticsearch. The second example is even simpler; now we know we need a service for subscribing and sending notifications.

Quality attributes

Some of them can be expressed in the definition of done or acceptance criteria for your tasks, stories and releases. Others can be expressed directly as user stories, as shown in the last example earlier.

Constraints

You can also check them as part of design and code reviews and create automated tests for some of them.

Recognizing architecturally significant requirements

Functional and Nonfunctional Requirements Chapter 3 You must prioritize requirements using two criteria: the business value and the impact on architecture. Late discovery of such requirements will often cost you time and money, as you will have to redesign part of your system, if not the entire solution.

Indicators of architectural significance

Enforcing compromises in meeting certain architectures: Your design decision may even have to compromise some requirements in favor of other, more important requirements if the cost is too high. Built-in apps should be designed differently than apps that run in the cloud, and apps developed by less experienced developers should likely use a simple and secure framework rather than using one with a steep learning curve or their own to develop.

Hindrances in recognizing ASRs and how to deal with them

If you want to find the best architecture given many trade-offs, be sure to read about the Architecture Trade-off Analysis Method (ATAM), which you can read about under one of the links in the Further Reading section. Some will become more important over time as the system grows and begins to communicate with more and more other systems.

Gathering requirements from various sources

If you are designing a service similar to Uber, some examples might be: when you receive a DriverSearchRequest, the system should respond quickly with an AvailableDrivers message, or the system should be available 24/7. With all this knowledge about how to distinguish the important requirements from the rest, you know what to look for.

Knowing the context

Phrases like these always require explanation, and it's often valuable to know what's behind them. If you already have some users but can't mine the data, it's often useful just to observe how they behave.

Knowing existing documentation

In addition to future risks, you must also be aware of current problems, such as a lack of subject matter experts in the company or high competition in the market. Other areas of application are the business rules that shape the day-to-day behavior of a company, as your product will likely need to adhere to and possibly improve on them.

Knowing your stakeholders

Gathering requirements from stakeholders

Apart from interviews, you can also organize workshops for them, which can function as brainstorming sessions. Once these are in place, you can proceed with consolidating similar ones, after which you should prioritize and finally process all the stories.

Documenting requirements

In such workshops, once the common ground has been established and everyone knows why they are participating in such an endeavor, you can ask everyone for as many use cases as they can think of. The last thing to note is this: you don't need to include all stakeholders in such events, as they can sometimes last more than a day depending on the size of the system.

Documenting the context

Documenting the scope

Documenting functional requirements

Detailed description: You can post any information you find relevant here, for example user stories. Deliver: If this requirement is required for any key date, you can record it here.

Documenting nonfunctional requirements

Managing the version history of your documentation

You can also add tags for specific revisions, such as first draft, if you so choose.

Documenting requirements in Agile projects

Functional and Nonfunctional Requirements Chapter 3 As you can see, the first two items are related to the feature we're going to do next. If you have to deal with external regulators or internal teams, such as auditing, legal or compliance, chances are they will still need a well-written physical document from you.

Other sections

There are cases, on the other hand, that still require you to have a complete list of requirements. Even if it takes a few iterations, it will help you have common ground with your stakeholders, so you'll gain more confidence that you're building the right thing.

Documenting architecture

For example, if you are interested in documenting enterprise architecture, you may be interested in TOGAF. If you're not documenting enterprise architectures, and especially if you're just starting out on your architectural self-development journey, you'll likely be more interested in other frameworks, such as the 4+1 and C4 models.

Understanding the 4+1 model

Reusability, tooling constraints, layering, modularization, packaging, runtime environments - this view can represent them by showing a system building block decomposition. This view shows whether the system is doing what it should and that it is consistent.

Figure 3.1 – An overview of the 4+1 model
Figure 3.1 – An overview of the 4+1 model

Understanding the C4 model

Just by looking at the diagram, it should be clear that there is one actor involved (the human-shaped representation of the customer), which interacts with one of the components of our solution, namely the customer service system. The context diagram we have described is used to provide an overview of the system with little detail.

Figure 3.7 – A C4 context diagram
Figure 3.7 – A C4 context diagram

Documenting architecture in Agile projects

Choosing the right views to document

The same approach applies to architecture: if you find another valid view of the document, you should do it. We'll describe them shortly here, but if you're interested, you should pick up the book by Rozanski and Woods mentioned in the Further Reading section.

Functional view

Also, if you built a smart house, chances are you'll want to draw a plan of the devices you want to place around. If you end up with a God object in your models, try rethinking the design and breaking it down into smaller, more cohesive pieces.

Information view

Functional and Nonfunctional Requirements Chapter 3 Another major benefit of including a functional view in your documentation is that it clarifies the responsibilities between the components of your system. One last important note about the functional view: try to keep every diagram you include at an abstraction level.

Concurrency view

Development view

Functional and non-functional requirements Chapter 3 Tactics to increase reuse in your system, such as creating your own runtime framework for components, or tactics to increase the coherence of your systems, such as a common approach to authentication, logging, internationalization, or other types of processing, are all part of the developmental view. A general approach to code organization, building, and configuration management should also go into this section of your documentation.

Deployment and operational views

If you have a specific installation and upgrade plan in mind, it might be a good idea to write a few words about it. If you can think of any possible errors that might occur, make a plan to detect and recover from them.

Generating documentation

If you have non-simple networking needs, you can add another diagram showing the network nodes and the connections between them. If you depend on specific technologies (including specific versions of software), it's a good idea to list them to see if there are any compatibility issues between the software you use.

Generating requirements documentation

Generating diagrams from code

Generating (API) documentation from code

The Design and 2

The different service models and when to use each one. How to avoid the fallacies of distributed computing. CAP Theorem Results and How to Achieve Ultimate Consistency Making your system fault tolerant and available.

Understanding the peculiarities of distributed systems

Different service models and when to use them

On-premises model

Infrastructure as a Service (IaaS) model

IaaS can be used in scenarios ranging from website hosting (can be cheaper than traditional web hosting), through storage (for example, Amazon's S3 and Glacier services), to high-performance computing and big data analysis (requires huge computing power). Using IaaS instead of on-premises infrastructure can be a cheap way to test new ideas while saving the time needed for configuration.

Platform as a Service (PaaS) model

In CaaS, instead of bare bones systems and virtual machines, the service gives you containers and orchestration capabilities that you can use to build your own container clusters. Architecture and System Design, Chapter 4 In addition to the more general PaaS, there is also Communications Platform as a Service (CPaaS), which provides you with the entire communications backend, including audio and video, that you can integrate into your solution.

Software as a Service (SaaS) model

With this technology you can easily provide video-enabled helpdesks or simply integrate live chats into your apps.

Function as a Service (FaaS) model and serverless architecture

If used correctly, FaaS abstracts the servers from the developers, can reduce your costs and can give you better scalability since it can be event based, not.

Avoiding the fallacies of distributed computing

The network is reliable

Latency is zero

Bandwidth is infinite

The network is secure

If your system is going to be online, it will be attacked and it is possible that a breach will occur at some point. It boils down to different controls for different parts of your system (your infrastructure, your applications, etc.) so that when a breach occurs, its reach and associated damage will be limited.

Topology doesn't change

There is one administrator

Try to model your system so that it is loosely coupled and backward compatible, so that component upgrades do not require other components to be upgraded as well. Decentralizing your system shouldn't mean you now have to manually look at logs on a dozen different machines.

Transport cost is zero

If you're looking for something lighter than Logstash, consider Fluentd and Filebeat, especially if you're dealing with containers.

The network is homogeneous

Try to limit the number of protocols and formats used, try to use standard ones, and avoid vendor lock-in to ensure your system can still communicate properly on it.

CAP theorem and eventual consistency

If your system needs to continue working under partitions, or you can allow ultimate consistency, go with AP. In a system that provides ultimate consistency, on the other hand, you only guarantee that after a write, a read will eventually see the change.

Sagas and compensating transactions

To process an order, above all other services, you need three: one for processing the order, one for booking the supplies, and one for loading the card. Now there are two ways the saga pattern can be implemented: choreography-based (also called event-based) and orchestration-based (also called command-based).

Choreography-based sagas

Orchestration-based sagas

Architecture and System Design Chapter 4 If a message is sent to the orchestrator that one of the services has failed, for example, if a credit card has expired, it should initiate a rollback. In our case, I would again use the broker to send the appropriate callback command to the specific service.

Making your system fault tolerant and available

Calculating your system's availability

To calculate the composite availability between two services that need to work together, you should just multiply their uptimes. To calculate the availability of redundant services (such as two independent regions), we should multiply their unavailability.

Building fault-tolerant systems

A common practice for cloud applications is to provide a service level agreement (SLA), which specifies how much downtime can occur per given period of time (for example, a year). An SLA for your cloud service will depend heavily on the SLAs of the cloud services you are building on.

Redundancy

Architecture and System Design Chapter 4 The previous mechanism is called active-passive (or master-slave) failover, because the backup server does not handle incoming traffic. For more information on active-active architectures, see the last link in the Further Reading section.

Leader election

Consensus

Replication

The latter does not require data redundancy, but can often give you excellent performance at scale.

Master-slave replication

Multi-master replication

Queue-based load leveling

Also, if your service is down, the requests will still be queued for said service to process when it recovers, so this can be a way to help bump up availability. If the requests come infrequently, consider implementing your service as a function that only runs when there are items in the queue to save costs.

Back pressure

Architectural and System Design Chapter 4 If your queue is performant and your tasks can be parallelized, a side benefit of this pattern would be better scalability. Keep in mind that when using this pattern, the overall delay will increase as the queue is added.

Detecting faults

Sidecar design pattern

Heartbeat mechanism

Leaky bucket counter

Minimizing the impact of faults

Retrying the call

Avoiding cascading failures

Circuit breaker

Bulkhead

Geodes

Integrating your system

Pipes and filters pattern

Instead, you can split order processing into separate steps, each handled by a separate component: one for decoding, one for validation, another for actually processing the order, and then another one for storing it somewhere. If you want to process several orders at the same time, you can also pipeline yours.

Competing consumers

With this approach, you can now independently perform each of these steps, easily replace or disable them if necessary, and reuse them for processing different types of incoming messages. To scale a step in your processing, you may want to use this model in conjunction with the next step in our list.

Transitioning from legacy systems

Anti-corruption layer

Strangler pattern

At the beginning of the migration process, the throttle facade will drive most of the requirements on the legacy system. During the migration, more and more calls can be forwarded to the new one, increasingly overwhelming the old system, limiting the functionality it provides.

Achieving performance at scale

After migration, you can still use the suppressor as an entry point or adapter for legacy requirements. This pattern can be overkill for small systems and can become problematic if the data store is shared or event driven.

CQRS and event sourcing

Command-query responsibility segregation

Architectural and System Design Chapter 4 Using the approach shown here, you can create as many different commands as you want, each with its own handler. Because of the complexity it presents, it is usually not suitable for small or less demanding architectures.

Figure 4.2 – CQRS with event sourcing
Figure 4.2 – CQRS with event sourcing

Command-query separation

As introduced in Chapter 2, Architectural Styles, event sourcing means that instead of constantly saving the entire state of your application, which might deal with conflicts between updates, you can only save the changes that have happened to your application's state. Using event sourcing can increase the efficiency of your application by eliminating concurrent updates and allowing all stakeholders to make incremental changes to their state.

Caching

Application Cache: To speed up your application, which can now read data from a cache instead of reaching out to its database. An alternative could also be to shard them: in the same way you would shard databases, you can use different instances of your caches for different parts of your data.

Updating caches

Client-side cache: For storing data specific to a given client, often placed on the client's machine or browser. Web server caching: To speed up reading from web pages, for example through HTTP accelerators such as Varnish that can cache the web server responses.

Write-through approach

Architectural and System Design Chapter 4 Caching works by simply storing the most frequently read data in non-persistent storage with fast access times. Some types of cache can be replicated or deployed in clusters to provide performance at scale.

121 ]Write-behind approach

Cache-aside

Deploying your system

The sidecar pattern

Deploying a service with tracing and a reverse proxy using Envoy

Architectural and System Design Chapter 4 We use our Dockerfile here, a simple grid, and expose two ports from the container to the host: our service and the admin interface. You can now run the service using docker-compose up --build and point your browser to the endpoints we specified.

Zero-downtime deployments

Using a sidecar proxy has another advantage: even if your service dies, the sidecar is usually still alive and can respond to external requests while the main service is down.

Blue-green deployments

Canary releases

When an update is implemented, the new version of a service will only be seen and used by a small fraction (here: 5%) of your users. If the updated instances remain stable and no checks and verifications fail, you can gradually update more and more hosts in multiple steps until they are all switched to a new version.

External configuration store

You can do this either by manually updating the configuration files or by using the admin endpoint. You can use ready-made solutions such as Firebase Remote Config, leverage the Java-based Netflix Archaius, or write a configuration store on your own leveraging cloud storage and change notifications.

Managing your APIs

This allows you to provide common settings for multiple instances and adjust parameters for several of them, while having a simple and centralized way. The easiest way to submit requests to your servers is by connecting directly to the services.

API gateways

For example, you can find common design patterns for Google Cloud Platform in the Further Reading section. In the next chapter, we'll learn how to use specific C++ features to take the path to architectural excellence in a more enjoyable and efficient way.

Designing great APIs

Leveraging RAII

Plus, we didn't have to write our own class; we just used the standard library's unique_ptr, which is a lightweight pointer. The C API itself does not need to be available to the user if you only want to use it from C++.

Specifying the interfaces of containers in C++

Having such type aliases can be useful even if you don't use them in your templates. For example, if you're writing a splitter, many of its consumers will rely on the presence of certain type aliases.

Using pointers in interfaces

For example, if you want to pass multiple instances, you can use a container, such as std::span. The D function should only be used if you want to pass in a unique_ptr containing one resource and receive another resource in the same unique_ptr as an out parameter.

Specifying preconditions and postconditions

Leveraging inline namespaces

Because the preceding code uses built-in namespaces, users will not see the difference between the two construction types when you declare objects of this class: all declarations from the built-in namespace are visible in the surrounding scope. For more tips on providing safe and elegant ABIs, see Arvid Norberg's talk The ABI Challenge from C++Now 2019, linked in the Further Reading section.

Leveraging std::optional

However, the linker will end up with different symbol names, which will cause the linker to fail if it tries to link incompatible libraries, giving us the ABI security we're looking for and a nice error message mentioning the inline namespace .

Optional function parameters

Optional function return values

Optional class members

Writing declarative code

Exploiting C++ Language Features Chapter 5 C++ is a flexible beast that allows you to write code either way. The second tactic for writing declarative code in C++ is already somewhat present in the previous excerpt.

Showcasing a featured items gallery

Now that we have those building blocks, let's figure out how to get the customer's favorite stores. Now that we're done with our basic implementation, let's see how we can improve it using some new language features from C++20.

Introducing standard ranges

Next, we need to convert each element to a gsl::not_null pointer so we can avoid unnecessary item copies. Once we have our lazy view, similar to our base implementation, we can reserve space in the vector and copy elements there from each nested vector in the all_featured view.

Reducing memory overhead and increasing performance using ranges

In our case, we just passed over another member of our item class so that it can be used for comparison when sorting. If you expected more from strings than just concise code, there's good news: in our case they can be used even more efficiently.

Moving computations at compile time

Now you can not only write plain simple constexpr functions thanks to the previous standards (quite an improvement over C++11's single expressions), but you can also use dynamic ones. As you can see, we are doing a binary search of a variety of traders, sorted by their IDs.

Helping the compiler help you by using const

But wait – you can tell that there's a function call in the assembly, so maybe we can inline it so it can be better optimized. Often this helps a lot, although now we only get the assembly inline (see: https://godbolt.org/z/hPadxd).

Leveraging the power of safe types

Constraining template parameters

What's important here is that you can use the requires clause to specify what code should be valid to call your type when it meets the requirements of a concept. Here you can see that a concept name can be used instead of the type name keyword in a template declaration.

Writing modular C++

You also know how to use types like std::optional to better express your intent in your interfaces. Using C++ Language Functions Chapter 5 We then demonstrated how to use functions such as generic and template lambdas, as well as if constexpr for writing less code that will work with many types.

Writing idiomatic C++

Automating scope exit actions using RAII guards

Here we take a timestamp at the start of the function and another at the end.

Managing copyability and movability

Implementing non-copyable types

Adhering to the rules of three and five

Adhering to the rule of zero

Now we have less boilerplate code, and looking at the members, it's easier to notice that it doesn't support copying. There's one more important idiom to know when it comes to copywriting, which you'll get to in a minute.

Using hidden friends

Using the std::swap line, the compiler first looks for the swap functions in . This is called the two-step ADL and swap idiom, or two-step for short, because we first make std::swap visible and then call swap.

Providing exception safety using the copy-and- swap idiom

Here, we use std::exchange so that our members are initialized and other members are cleared, all in the initialization list. We've created a group class that provides strong exception safety with little effort and no code duplication.

Writing niebloids

Let's tackle another C++ idiom that can be seen in a few places in the standard library. We require that the scope iterator after the projection call can be compared for equality to objects of type T, similar to before.

Policy-based design idiom

Since our policies are stateless, we can use it whenever we need to without additional cost. To do this, you will need to implement the assignment operators as template functions with the policy as the template parameter.

Curiously recurring template pattern

Knowing when to use dynamic versus static polymorphism

Implementing static polymorphism

Each of these classes derives from our GlamorousItem base class using itself as a template argument. Note that, as opposed to dynamic polymorphism, the base class in CRTP is a template, so you'll get a different base type for each of your derived classes.

Interlude – using type erasure

Gambar

Figure 1.1 – Two bounding contexts with the matching terms mapped between them (image from one of Martin Fowler's articles on DDD: https://martinfowler.com/bliki/BoundedContext.html)
Figure 1.2 – Coupling versus cohesion
Figure 2.1 – The mediator topology
Figure 2.2 – The broker topology
+7

Referensi

Dokumen terkait