Blazor
Introduction
A robust software architecture design represents a considerable challenge in applying advanced concepts of the object-oriented programming paradigm, including a correct definition of abstractions that faithfully correspond to complex business requirements presented in real scenarios. There are
numerous solutions based on the level of complexity that needs to be applied when software is built, and in this chapter, you will have the opportunity to understand the Abstract Factory Pattern, an important approach to simplify the creation of complex objects.
Learning the Abstract Factory Pattern allows you to elegantly apply the best practices of object-oriented programming regarding polymorphism and encapsulation while developing a stable structure to extend and scale the complexity of a project that quickly supports requirement changes. Knowing this design pattern is crucial to familiarize yourself with other creational patterns used in the market.
With this chapter, you will have the opportunity to understand the Abstract Factory Pattern, applying its concept in a step-by-step approach for a Blazor application.
Structure
In this chapter, we will discuss the following topics:
Abstract Factory Pattern concept Examples in C# and .NET
Practical implementation of Abstract Factory in Blazor applications
Objectives
After studying this unit, you should be able to understand the Abstract Factory Pattern, apply the Abstract Factory Pattern in real-world projects, and identify the use of encapsulation, polymorphism, and other object- oriented programming concepts.
Abstract Factory definition
Any design pattern in software development represents a general solution for common situations that frequently happen in recurring scenarios, being a robust alternative to building a software architecture that follows the most recognized approach used widely in the market. All software projects vary in business and non-functional requirements, but many have similar technical challenges.
For instance, imagine a scenario where a system has the object of allowing users to register products and services for an online store. Different
companies sell different types of products, but the infrastructure behind the implementation of this kind of system is quite similar in terms of classes, business logic, data models, database structure, and technical requirements, including the need to control transactions and support the registration of a considerable amount of entries that contain different configuration in terms of properties and characteristics.
Therefore, it is possible to say that there is a pattern followed by any online store, which requires building and supporting the creation and management of complex records, many families of products, and the ability to provide a particular behavior of the system in terms of business requirements and workflow execution based on each product type.
In this context, among the list of all the available design patterns available, there is the Abstract Factory Pattern, which allows us to create families of related objects, abstracting the complexity of the creation of those from the part of the application that is consuming these objects. In summary, in the case where the system needs to create objects that contain distinct ways to create their corresponding properties based on sophisticated rules, the Abstract Factory Pattern encapsulates the implementation and the logic behind the construction of objects from the concrete classes, giving us the flexibility of extending the software architecture by inserting more types of objects of the same family, without compromising the integrity of the
implementation and consistency in the application.
As demonstrated previously in this book, the correct use of interfaces in software development based on the object-oriented programming paradigm is one of the essential characteristics of having a testable, extensible, and reliable structure, representing one of the critical points of the Abstract Factory Pattern.
Abstract Factory scenario
Considering the Abstract Factory Pattern reduces the complexity of the creation families of objects, it is essential to apply its concepts in projects that have requirements similar to the problems that the Abstract Factory Pattern is meant to solve. Therefore, in this section, you will have the opportunity to implement the base structure of a mobile company that sells various types of plans to customers related to text messages, mobile data, and other services, which represents a scenario that the Abstract Factory Pattern can solve with the following requirements:
The company will offer two types of plans for customers: pre-paid and post-paid
Both plans must have different conditions for text messages, internet connection speed, and mobile data limits
The pre-paid option has a limited plan for text messages, up to 1,000 messages monthly
The pre-paid option has a maximum speed of 10 megabytes per second
The pre-paid plan has a 10 gigabytes limit for data transfer within the same month
The postpaid option offers unlimited data transfer and an unlimited number of text messages
The postpaid plan has a high-speed internet connection, up to 500 megabytes per second
The requirements clearly show that the mobile plans have the same structure in terms of properties for the objects that would be defined in the application using C# language, but the type and the state of the objects are slightly
distinct, as shown in Table 7.1:
Mobile plan Text message limit Mobile data transfer Connection speed Pre-paid 1,000/month 10 gigabytes/month 10 megabytes/sec
Postpaid Unlimited Unlimited 500 megabytes/sec
Table 7.1: Mobile plans
Given that scenario, the use of the Abstract Factory Pattern allows us to build these complex objects by abstracting their implementation from the highest layer of the application, which is in this pattern, the Client layer, as shown in Figure 7.1:
Figure 7.1: Abstract Factory implementation
The correlation between the mobile plan and the type of compound objects is highlighted in Figure 7.1, with blue for the postpaid plan and yellow for the pre-paid plan. The Abstract Factory is known in the market as the factory of factories pattern, once the subset of objects created by the main factory is other factories.
Abstract Factory implementation
Considering the requirements and model structure established in the given scenario, the first step for implementing the Abstract Factory pattern is to create individual classes and interfaces. In the real example of this chapter, a Blazor application is used as the client application, and the interaction with the database is mocked using objects in the memory. Following the diagram demonstrated in Figure 7.1, the first class must be created and defined for each factory, starting with the Text Message Factory and its underlying interface, with the scope limited to the representation shown in Figure 7.2:
Figure 7.2: Text Message Factory representation
The implementation of the Text Message Factory requires an interface that includes properties related to the underlying concrete type for the category (unlimited and 1,000/month), as shown in Figure 7.3:
Figure 7.3: Text Message Factory interface
The interface contains only two properties, which aim to identify the
difference between the objects when other classes in the given scenario will
consume them. Creating interfaces for the Abstract Factory classes is a good practice in terms of future testability and extensibility for any project, and the next step in our example is to create the concrete class that
implements the given interface for the Text Message Factory, as shown in Figure 7.4:
Figure 7.4: Thousand Text Message class
As the given scenario contemplates two types of text message plans (unlimited and a thousand messages), the second class regarding the unlimited type must be created, implementing the same interface
ITextMessageFactory, as shown in Figure 7.5:
Figure 7.5: Unlimited Text Message class
Considering both types implement the same interface, they have the same members and methods. However, each can implement a distinct behavior based on each specification. This approach allows us to create units and tests consistently, mock integration with databases, inject data and extend the functionality to other types without breaking the existing implementation.
As stated in the requirements for the scenario in this chapter, a mobile plan must contain information on text messages, internet connection, and mobile data plan, as shown in the list of requirements at the beginning of this
section. Considering the infrastructure for the text message is already created, the next step is to create the Internet Connection Factory, which should follow the structure presented in Figure 7.6:
Figure 7.6: Connection Speed Factory representation
The implementation of the Connect Speed Factory requires an interface that includes properties related to the concrete type (high speed and low speed), using interfaces as shown in Figure 7.7:
Figure 7.7: Connection Speed Factory interface
The interface contains only two properties (Name and Velocity), which aim to identify the difference between the objects that the implementation class will consume. The next step is to create the concrete class that implements the interface for the Connection Speed Factory, as shown in Figure 7.8:
Figure 7.8: Low-Speed Factory class
As the given scenario contemplates two types of connection speed (high speed and low speed), the second class regarding the high-speed type must be created, implementing the same interface IconnectionSpeedFactory, as shown in Figure 7.9:
Figure 7.9: High-speed class
Considering both types share the same interface, it is mandatory to
implement the members and methods with similar signatures, but each of the classes might implement a distinct behavior based on their specification. The mobile plan requires the creation of an extra and last factory class
responsible for handling the creation of the mobile data transfer information, which must have two different types, as shown in Figure 7.10:
Figure 7.10: Mobile Data Factory representation
The implementation of the Mobile Data Factory requires the creation of an interface that includes the properties related to the future concrete types (unlimited and 10 Gb/month), sharing the same interface, as shown in Figure 7.11:
Figure 7.11: Mobile Data Factory interface
The interface has two main properties (name and limit), which aim to identify the difference between the different types of objects. The next step is to specify the concrete class that implements the interface for the Mobile Data Factory, as shown in Figure 7.12: