Transparent façade – Structural Patterns

The transparent façade is the most flexible type of façade and is exceptionally suitable for a system that leverages dependency injection. The implementation is similar to the opaque façade, but the public visibility modifier changes how consumers can access the class library elements. For this system, it was worth adding interfaces to allow the consumers of the subsystem to extend it when needed.First, let’s take a look at the abstractions:

namespace TransparentFacadeSubSystem.Abstractions
{
    public interface ITransparentFacade
    {
        string ExecuteOperationA();
        string ExecuteOperationB();
    }
    public interface IComponentA
    {
        string OperationA();
        string OperationB();
    }
    public interface IComponentB
    {
        string OperationC();
        string OperationD();
    }
    public interface IComponentC
    {
        string OperationE();
        string OperationF();
}

The API of this subsystem is the same as the opaque façade. The only difference is how we can use and extend the subsystem (from a consumer standpoint). The implementations are mostly the same as well, but the classes implement the interfaces and are public; the highlighted elements represent those changes:

namespace TransparentFacadeSubSystem
{
    public class ComponentA : IComponentA
    {
        public string OperationA() => “Component A, Operation A”;
        public string OperationB() => “Component A, Operation B”;
}
    public class ComponentB : IComponentB
    {
        public string OperationC() => “Component B, Operation C”;
        public string OperationD() => “Component B, Operation D”;
    }
    public class ComponentC : IComponentC
    {
        public string OperationE() => “Component C, Operation E”;
        public string OperationF() => “Component C, Operation F”;
    }
    public class TransparentFacade : ITransparentFacade
    {
        private readonly IComponentA _componentA;
        private readonly IComponentB _componentB;
        private readonly IComponentC _componentC;
    public TransparentFacade(IComponentA componentA, IComponentB
componentB, IComponentC componentC)
    {
        _componentA = componentA ??
throw new ArgumentNullException(nameof(componentA));
        _componentB = componentB ??
throw new ArgumentNullException(nameof(componentB));
        _componentC = componentC ??
throw new ArgumentNullException(nameof(componentC));
    }
        public string ExecuteOperationA()
        {
            return new StringBuilder()
                .AppendLine(_componentA.OperationA())
                .AppendLine(_componentA.OperationB())
                .AppendLine(_componentB.OperationD())
                .AppendLine(_componentC.OperationE())
                .ToString();
            }
        public string ExecuteOperationB()
        {
            return new StringBuilder()
                .AppendLine(_componentB.OperationC())
                .AppendLine(_componentB.OperationD())
                .AppendLine(_componentC.OperationF())
                .ToString();
        }
    }
}

To simplify the use of the subsystem, we create the following extension method as a good practice that makes consuming the subsystem easier. Everything that we define in that method can be overridden from the composition root (which is not the case for the opaque façade):

public static class StartupExtensions
{
    public static IServiceCollection AddTransparentFacadeSubSystem(this IServiceCollection services)
    {
        services.AddSingleton<ITransparentFacade, TransparentFacade>();
        services.AddSingleton<IComponentA, ComponentA>();
        services.AddSingleton<IComponentB, ComponentB>();
        services.AddSingleton<IComponentC, ComponentC>();
        return services;
    }
}

All the new elements are gone and have been replaced by simple dependency registration (singleton lifetimes, in this case). These little differences give us the tools to reimplement any part of the subsystem if we want to, as we cover soon.

We can register bindings in the transparent façade extension method because classes and interfaces are public. The container needs a public constructor to do its work.

In the opaque façade, we had to define the constructor of the OpaqueFacade class as internal because the type of its parameters (ComponentA, ComponentB, and ComponentC) are internal, making it impossible to leverage the container. Changing the visibility modifier of the opaque façade constructor from internal to public would have yielded a CS0051 Inconsistent accessibility error.

Besides those differences, the transparent façade plays the same role as the opaque façade, outputting the same result.We consume those two façades next.

Leave a Reply

Your email address will not be published. Required fields are marked *



          Copyright © 2015-2024 | About | Terms of Service | Privacy Policy