简体   繁体   中英

Composite pattern of sealed class that has no interface

Let's say that I am using a library that I have no control over whatsoever. This library exposes service that requires argument of certain class. Class is marked as sealed and has no interface .

tl;dr: How can I reimplement sealed class as interface ?

Code example:

using System;

namespace IDontHaveControlOverThis
{
    // Note no interface and the class is being sealed
    public sealed class ArgumentClass
    {
        public String AnyCall() => "ArgumentClass::AnyCall";
    }

    public sealed class ServiceClass
    {
        public String ServiceCall(ArgumentClass argument) => $"ServiceClass::ServiceCall({argument.AnyCall()})";
    }
}

namespace MyCode
{
    // Composite pattern, basically I need: "is a ArgumentClass"
    // Obviously doesn't work - can't extend from sealed class
    public class MyArgumentClass : IDontHaveControlOverThis.ArgumentClass
    {
        private IDontHaveControlOverThis.ArgumentClass arg = new IDontHaveControlOverThis.ArgumentClass();

        public String AnyCall() => $"MyArgumentCLass::AnyCall({arg.AnyCall()})";
    }
}

public class Program
{
    public static void Main()
    {
        // I don't have control over this
        IDontHaveControlOverThis.ServiceClass service = new IDontHaveControlOverThis.ServiceClass();


        //This obviously works
        IDontHaveControlOverThis.ArgumentClass arg = new IDontHaveControlOverThis.ArgumentClass();
        Console.WriteLine($"Result: {service.ServiceCall(arg)}");

        // How to make this work?
        IDontHaveControlOverThis.ArgumentClass myArg = new MyCode.MyArgumentClass();
        Console.WriteLine($"Result: {service.ServiceCall(myArg)}");
    }
}

The compiler error message

Cannot implicitly convert type 'MyCode.MyArgumentClass' to 'IDontHaveControlOverThis.ArgumentClass'

note: emphasis mine

should give you a hint as to what you can do

public class MyArgumentClass {
    private IDontHaveControlOverThis.ArgumentClass arg = new IDontHaveControlOverThis.ArgumentClass();

    public String AnyCall() => $"MyArgumentCLass::AnyCall({arg.AnyCall()})";


    public static implicit operator IDontHaveControlOverThis.ArgumentClass(MyArgumentClass source) {

        return source.arg;
    }
}

So now your "wrapper" exposes the 3rd party dependency as needed

 IDontHaveControlOverThis.ArgumentClass myArg = new MyCode.MyArgumentClass();

or directly

var myArg = new MyCode.MyArgumentClass();
Console.WriteLine($"Result: {service.ServiceCall(myArg)}");

Reference User-defined conversion operators (C# reference)

Which can allow for abstracting your code

namespace MyCode {

    public interface IMyService {
        String ServiceCall(MyArgumentClass argument);
    }

    public class MyServiceClass : IMyService {
        public string ServiceCall(MyArgumentClass argument) {
            IDontHaveControlOverThis.ServiceClass service = new IDontHaveControlOverThis.ServiceClass();
            return service.ServiceCall(argument);
        }
    }
}

Based on the code sample you show, the answer is you can't . You need to be able to modify the behavior of IDontHaveControlOverThis.ArgumentClass , by setting a property, or creating a new instance with different constructor parameters in order to modify the servicecall. (It now always returns the same string, so the servicecall is always the same)

If you are able to modify the behavior of the ArgumentClass by setting properties. You could create wrappers for the sealed classes in your own code, and use that throughout your codebase.

public class MyArgumentClass
{
        // TODO: Set this to a useful value of ArgumentClass.
    internal IDontHaveControlOverThis.ArgumentClass InnerArgumentClass { get; }
    public virtual string AnyCall() => "???";
}

public class MyServiceClass
{
    private IDontHaveControlOverThis.ServiceClass innerServiceClass
            = new IDontHaveControlOverThis.ServiceClass();

    public virtual string ServiceCall(MyArgumentClass argument)
    {
        return innerServiceClass.ServiceCall(argument.InnerArgumentClass);
    }
}

or

public class MyArgumentClass
{
    public virtual string AnyCall() => "???";
}

public class MyServiceClass
{
    private IDontHaveControlOverThis.ServiceClass innerServiceClass
            = new IDontHaveControlOverThis.ServiceClass();

    public string ServiceCall(MyArgumentClass argument)
    {
        var serviceArgument = Convert(argument);
        return innerServiceClass.ServiceCall(serviceArgument);
    }

    private IDontHaveControlOverThis.ArgumentClass Convert(MyArgumentClass argument)
    {
        // TODO: implement.
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM