Structural Design Patterns in C#

What is the Structural Design Pattern in C#?

According to Wikipedia, structural design patterns simplify software engineering by pointing out an easy method for realizing the relationships between things. To put it simply, Structural Design patterns are primarily used to manage the interface and class structures and the relationships between the classes.

When to use ?

In real-time applications, it is occasionally necessary to modify a class's structure or the relationships between its classes, but we don't want the project to interfere with these changes. Additionally, one-to-many relationships between the Product and User are created by using the Product class inside the User class.

The arrangement of these two classes or their relationships will change tomorrow. The Structural Design Pattern comes in handy in this situation.

What is the Adapter Design Pattern?

A structural design called the Adapter design allows objects with conflicting interfaces to cooperate. This technique can be helpful when you wish to use preexisting classes, but their interfaces need to match your needs.

Functioning as a link between two incompatible things is the Adapter Design Pattern. Assume that A is the first object and B is the second. A portion of object B's services is what object A wants to use. Under these circumstances, the Adapter will enter the scene and serve as a mediator or bridge between items A and B. Object A will now call the Adapter, which will subsequently call object B after completing any required modifications or conversions.

Example:

Next, we will compare the adapter design pattern's UML diagram to our example. Please review the picture that follows. Two systems, or more accurately, two interfaces, are visible here. The Third Party Billing System is seen on the right, while the Client, or the Current HR System, is visible on the left. We will now examine the incompatibilities between these two systems and how we may use C#'s Adapter Design Patterns to make them compatible.

Structural Design Patterns in C#

As you can see, ProcessSalary is one feature offered by the Third Party Billing System. The employee list, or List, will be the input argument for this ProcessSalary method. It will then loop through each employee, compute their salary, and deposit the money into their bank account.

The employee data is kept as a string array on the left-hand side or in the current HR system. The HR system wants to handle employee salary processing. The HR system must then call the Third Party Billing System's ProcessSalary method.

However, if you look at the HR system, you'll see that the data about the employees is kept in a string array, whereas the Third Party Billing System's ProcessSalary method requires data to be in a List. Due to the incompatibility between the List and string array, the HR System is unable to make direct calls to the Third Party Billing System. As a result, these two systems cannot coexist.

How can we make to work together?

Using the C# Adapter Design Pattern, these two systems or interfaces can function together. As seen in the graphic below, we must add an adapter between the Third-Party Billing and HR systems.

Structural Design Patterns in C#

The HR system will now send the employee data to the adapter as a String Array. This adapter will read the data from the string array, filling the employee object and adding each employee object to the List collection. Subsequently, the Adapter will forward the List to the Third-Party Billing System's ProcessSalary function. The salary for each employee is then determined using the ProcessSalary technique and deposited into the employee's bank account.

Thus, we can use the Adapter Design Pattern in C# to make two incompatible interfaces operate together. Once again, there are two approaches to implementing the Adapter Design Pattern in C#.

Implementation:

Now, let's go on and examine how the Object Adapter Design Pattern can be implemented in C#.

Step1: Creating Employee Class

The following code should be copied and pasted into a class file called Employee. cs. Both the Adapter and ThirdPartyBillingSystem (i.e., Adaptee) will use this class. Here, we first constructed the Employee with the necessary parameters, and then we used the class constructor to initialize the properties.

Step 2: Creating Adaptee

The functionality that the customer needs will be included in this class. Nevertheless, this interface needs to be more client-compatible. In order to use this, create a class file called ThirdPartyBillingSystem.cs and insert the code below. The ProcessSalary method in this class processes each employee's salary after receiving a list of employees as an input argument.

Step 3: Creating ITarget interface

In other words, make an interface called ITarget.cs and paste the code below into it. This class defines the abstract ProcessCompanySalary method, which the Adapter will implement. Once more, the client will process the salary using this method.

Step 4: Create an Adapter

Thus, create a class file called EmployeeAdapter.cs and paste the code below into it. This class offers the ProcessCompanySalary method's implementation as well as the ITarget interface. Additionally, the ThirdPartyBillingSystem (Adaptee) object is referenced in this class. The employee information is first received as a string array by the ProcessCompanySalary method, which transforms it into a list of employees. The list of employees is then passed as an argument to the ProcessSalary method on the ThirdPartyBillingSystem (Adaptee) object.

Step 5: Client

Our HR System, or the main method of the program class, will be the customer in this case. Kindly adjust the Main method as indicated below. You'll see that the employee data is currently stored in a string array. Next, we make an EmployeeAdapter object and invoke the ProcessCompanySalary function with the string array as a parameter. So, the Third Party Billing System and the Client now collaborate with the aid of the Adapter, also known as the EmployeeAdapter object.

UML Diagram :

Please refer to the following diagram to comprehend the Class or UML diagram and the many elements of the Object Adapter Design Pattern. As you can see, the client makes an instance of the Adapter using the ITarget Interface and then uses that instance to connect with the Adaptee. The adapter is the element that enables the use of two disparate, incompatible interfaces.

Structural Design Patterns in C#

There are four parts to the Adapter Design Pattern. They are listed in the following order:

Client: The class that implements the ITarget interface, or the Adapter (in our case, the EmployeeAdapter), is the only thing that the Client class is able to see.

ITarget: The Adapter will need to implement this particular interface. Only this interface-that is, the class that implements it-is visible to the client.

Adapter: This class enables the cooperation of two incompatible systems or interfaces. It implements both the interface method and the ITrager interface.

Adaptee: Therefore, it needs to be modified or transformed before the client may utilize it. It indicates that the Adapter will take the call from the client, perform the necessary conversions, and then call the Adaptee.

This is all related to the C# Object Adapter Design Pattern. Let's continue and examine how to accomplish the same thing in C# using the Class Adapter Design Pattern.

Understanding Class Adapter Design Pattern in C#:

Here is an additional method for applying the C# Adapter Design Pattern. The Adapter calls in this method will inherit from the Adaptee class and implement the ITarget interface. As a result, the Adapter class will now be the Adaptee class's child. As a result, since the Adaptee method is accessible through inheritance, it can be used directly rather than requiring the creation of an Adaptee reference variable.

Structural Design Patterns in C#

The class diagram is the same as the class diagram for the Object Adapter Design Pattern. Using the reference to the Adaptee object that the adapter possesses, it will call the adaptee methods in the instance of the Object Adapter Design Pattern.

Implementation:

The implementation is identical to the implementation of the Object Adapter Design Pattern.

The EmployeeAdapter class is the only area of distinction. Following any necessary transformations or discussions, we must invoke the ProcessSalary method.

Therefore, in order to use the C# Class Adapter Design Pattern, please edit the EmployeeAdapter class as indicated below. The ThirdPartyBillingSystem class, which is the Adaptee in this case, is the source of the EmployeeAdapter class, which implements the ITarget interface.

When to use the Object Adapter Pattern and Class Adapter Pattern in C#?

For instance, you cannot make inheritance work with a Java class; instead, you must utilize the Object Adapter Design Pattern to make the Java class compatible with the Dot Net class. However, you should use the Class Adapter Design Pattern if inheritance is allowed, both classes are part of the same project, and they are written in the same programming language.

Real-Time Applications?

  • Reusing Existing Code: An adapter can fill the gap if you have classes that already exist and provide functionality that you need to use, but their interfaces differ from the ones your system uses.
  • Developing the same Interface for Various Classes: When you wish to handle multiple classes consistently using the same interface, but they have various interfaces.
  • Supporting Multiple Data Sources: This refers to the situation in which your application needs to process data uniformly while handling it from several sources (such as databases, file systems, and web services).
  • Testing and Mocking: When real objects (such as database connections or external services) are difficult to use in a test environment, adapters can be used to build stubs or mocks for unit testing.
  • Maintaining Backward Compatibility: Adapters can be used to keep older APIs or data models compatible with updated applications or libraries.
  • Cross-Platform Compatibility: Used in situations when you must maintain consistency in the application code while supporting several environments or platforms.

Let's analyze each of these C# structural design patterns in more detail:

Adapter Pattern: If you wish to combine classes that already exist but have different interfaces without changing the source code, the Adapter pattern comes in handy.

In the given example:

Adaptee: The current class with an incompatible interface is known as the adaptee.

This pattern can be used to reuse existing classes in a new environment without changing their code or to integrate new components into an already-existing system.

Composite Pattern:

The composite pattern is utilized when representing objects in tree structures because it enables clients to handle both individual objects and object combinations consistently.

In the given example:

Component: This serves as the composition's overall object's interface.

Leaf: This stands in for the composition's leaf objects, which are childless.

Composite: This is a representation of a composite object that is capable of having children and that is delegated to them in order to implement their behavior.

This pattern comes in handy when you wish to handle individual objects and groups of objects consistently and you have hierarchical structures, such as file systems or GUIs.

Decorator Pattern:

Using the decorator pattern, you may dynamically add behavior to particular objects while preserving the behavior of other objects in the same class. It's a versatile substitute for subclassing for adding new features.

In the given example:

Component: This is the object interface to which responsibilities can be attached.

ConcreteComponent: This is an example of the base object to which other duties may be applied.

Decorator: The foundation class for decorators, which holds a reference to a component, is called Decorator.

ConcreteDecorator: This is the class of concrete decorators that gives the Component additional duties.

This approach comes in handy when you wish to add or change behavior without altering the fundamental implementation of a series of classes, each with distinct responsibilities.

Conclusion:

In Conclusion, C# structural design patterns provide effective ways to arrange and control the connections between classes and objects in your codebase. Including these patterns in your design makes your codebase easier to maintain, more extensible, and easier to understand. It also helps to organize your codebase. You may create systems that are simpler to create, manage, and adapt over time by utilizing these principles. The quality and resilience of your C# projects can be greatly increased by comprehending and utilizing these structural design patterns, regardless of the size of the system or application you're developing.






Latest Courses