Переопределение единственного метода интерфейса, когда реализующий класс запечатан - PullRequest
4 голосов
/ 19 августа 2011

Это, вероятно, проще всего объяснить с помощью кода (это, конечно, не фактический код, но он имеет те же свойства):

У меня есть интерфейс, который выглядит примерно так:

    public interface ISomeProvider
    {
        object GetFoo1(); //<-- This needs caching

        //These others don't
        object GetFoo2();
        object GetFoo3();

        //And let's say 20 more
    }

И это имеет такую ​​реализацию:

    //NOTE: Sealed class otherwise we could inherit from it
    public sealed  class SuperCleverProvider : ISomeProvider
    {
        public object GetFoo1()
        {
            return "a";
        }

        public object GetFoo2()
        {
            return "b";
        }

        public object GetFoo3()
        {
            return "b";
        }
    }

Теперь один из этих вызовов, скажем, GetFoo1, действительно тяжелый, поэтому я хочу предоставить новую версию интерфейса, где вызовы к нему кэшируются, используя экземпляр старого.

Я сейчас так делаю:

    public class CachedSuperCleverProvider : ISomeProvider
    {
        private readonly SuperCleverProvider _provider;

        public CachedSuperCleverProvider(SuperCleverProvider provider)
        {
            _provider = provider;
        }

        private object UsingCache<T>(string cacheKey, Func<T> eval)
        {
            //Pretend this does caching. This is not related to the question
            throw new NotImplementedException();
        }

        public object GetFoo1()
        {
            return UsingCache("foo1", _provider.GetFoo1);
        }

        //The code below this point is what I want to get rid of
        public object GetFoo2()
        {
            return _provider.GetFoo2();
        }

        public object GetFoo3()
        {
            return _provider.GetFoo3();
        }

        //And so on for all the rest
    }

У этого есть две проблемы (по крайней мере):

  • Каждый раз, когда кто-то добавляет метод в интерфейс, я должен изменить его, даже если я не хочу, чтобы этот новый метод кэшировался
  • Я получаю этот огромный список бесполезного кода, который просто вызывает основную реализацию.

Может кто-нибудь придумать способ сделать это, у которого нет этих проблем?

Ответы [ 2 ]

3 голосов
/ 19 августа 2011

Три варианта:

  • Автогенерация класса
  • Использование PostSharp или что-то похожее, чтобы сделать это на основе перехватчиков
  • Живи с этим

Лично я бы, вероятно, выбрал третий вариант, если только ты действительно не сделаешь это много.Взвесьте стоимость каждого варианта - сколько времени вы на самом деле потратите на добавление этой делегации?

Лично я бы хотел бы , чтобы увидеть подобные вещи.в качестве языковой функции - «делегируйте этому интерфейсу через это поле, если я не переопределю его», но, очевидно, этого сейчас нет ...

0 голосов
/ 19 августа 2011

Вот что я бы предложил. Это не намного лучше, но упростит процесс упаковки.

Создать класс SomeProviderWrapper:

public class SomeProviderWrapper : ISomeProvider
{
    protected ISomeProvider WrappedProvider { get; private set; }

    protected SomeProviderWrapper(ISomeProvider wrapped)
    {
        if (wrapped == null)
            throw new ArgumentNullException("wrapped");

        WrappedProvider = wrapped;
    }

    public virtual object GetFoo1()
    {
        return WrappedProvider.GetFoo1();
    }

    public virtual object GetFoo2()
    {
        return WrappedProvider.GetFoo2();
    }

    public virtual object GetFoo3()
    {
        return WrappedProvider.GetFoo3();
    }
}

Теперь, когда обёртка перенесена в собственный класс, вы можете написать версию для кэширования:

public class CachedSuperCleverProvider : SomeProviderWrapper
{
    public CachedSuperCleverProvider(ISomeProvider wrapped) : base(wrapped) { }

    private object UsingCache<T>(string cacheKey, Func<T> eval)
    {
        throw new NotImplementedException();
    }

    public override object GetFoo1()
    {
        return UsingCache("foo1", WrappedProvider.GetFoo1);
    }
}

Это удерживает код делегирования от вашего супер-умного провайдера. Вам все равно придется поддерживать код делегирования, но он не будет загрязнять дизайн вашего поставщика кэширования.

...