Как сделать 2 несовместимых типа, но с одинаковыми элементами, взаимозаменяемыми? - PullRequest
4 голосов
/ 19 марта 2010

Вчера 2 парня из нашей команды пришли ко мне с необычной проблемой. Мы используем сторонний компонент в одном из наших приложений winforms. Весь код уже написан против этого. Затем они захотели включить в наше приложение другой сторонний компонент от того же продавца. К их удовольствию они обнаружили, что второй компонент имеет те же общедоступные члены, что и первый. Но, к их ужасу, эти 2 компонента имеют совершенно разные иерархии наследования и не имеют общих интерфейсов. Заставляет задуматься ... Ну, заставляет меня задаться вопросом.

Пример проблемы:

Несовместимые типы http://www.freeimagehosting.net/uploads/f9f6b862f1.png

public class ThirdPartyClass1
{
    public string Name
    {
        get
        {
            return "ThirdPartyClass1";
        }
    }

    public void DoThirdPartyStuff ()
    {
        Console.WriteLine ("ThirdPartyClass1 is doing its thing.");
    }
}

public class ThirdPartyClass2
{
    public string Name
    {
        get
        {
            return "ThirdPartyClass2";
        }
    }

    public void DoThirdPartyStuff ()
    {
        Console.WriteLine ("ThirdPartyClass2 is doing its thing.");
    }
}

Радостно, что они чувствовали, что копирование и вставка кода, который они написали для первого компонента, не было правильным ответом. Таким образом, они подумали о том, чтобы мгновенно присвоить компоненту ссылку на объект, а затем изменить код для выполнения условного приведения после проверки, какой это был тип. Но это, возможно, даже страшнее, чем метод копирования и вставки.

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

Ответы [ 4 ]

12 голосов
/ 19 марта 2010

Мой первый вопрос: закрыты ли 2 класса сторонних компонентов? Они не были. По крайней мере, у нас это есть.

Итак, поскольку они не запечатаны, проблема решается следующим образом:

Извлечение общего интерфейса из совпадающих членов 2-х сторонних классов. Я назвал это Icommon.

public interface ICommon
{
    string Name
    {
        get;
    }

    void DoThirdPartyStuff ();
}

Затем создайте 2 новых класса; DerivedClass1 и DerivedClass2, которые наследуются от ThirdPartyClass1 и ThirdPartyClass2 соответственно. Эти два новых класса реализуют интерфейс ICommon, но в остальном они полностью пусты.

public class DerivedClass1
    : ThirdPartyClass1, ICommon
{
}

public class DerivedClass2
    : ThirdPartyClass2, ICommon
{
}

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

альтернативный текст http://www.freeimagehosting.net/uploads/988cadf318.png

Так что теперь вместо того, что у нас было ранее:

ThirdPartyClass1 c1 = new ThirdPartyClass1 ();
c1. DoThirdPartyStuff ();

Теперь мы можем сделать:

ICommon common = new DerivedClass1 ();
common. DoThirdPartyStuff ();

И то же самое можно сделать с DerivedClass2.

В результате весь наш существующий код, который ссылается на экземпляр ThirdPartyClass1, можно оставить как есть, просто заменив ссылку ThirdPartyClass1 на ссылку ICommon. Ссылке ICommon можно было бы затем дать экземпляр DerivedClass1 или DerivedClass2, который, конечно, в свою очередь наследуется от ThirdPartyClass1 и ThirdPartyClass2 соответственно. И все просто работает.

Я не знаю, есть ли конкретное имя для этого, но для меня это выглядит как вариант шаблона адаптера.

Возможно, мы могли бы решить проблему с динамическими типами в C # 4.0, но это не имело бы преимущества проверки во время компиляции.

Мне было бы очень интересно узнать, есть ли у кого-нибудь другой элегантный способ решения этой проблемы.

4 голосов
/ 19 марта 2010

Если вы используете .Net 4, вы можете избежать этого, так как динамический тип может помочь с тем, что вы хотите. Однако, если вы используете .Net 2+, есть другой (другой) способ достижения этого:

Вы можете использовать библиотеку утиной типизации, такую ​​как Deft Flux , чтобы обрабатывать сторонние классы так, как будто они реализовали интерфейс.

Например:

public interface ICommonInterface
{
    string Name { get; }
    void DoThirdPartyStuff();
}

//...in your code:
ThirdPartyClass1 classWeWishHadInterface = new ThirdPartyClass1()
ICommonInterface classWrappedAsInterface = DuckTyping.Cast<ICommonInterface>(classWeWishHadInterface);

classWrappedAsInterface.DoThirdPartyStuff();

Это позволяет избежать необходимости создания производных классов-оболочек вручную для всех этих классов и будет работать до тех пор, пока класс имеет те же члены, что и интерфейс

3 голосов
/ 19 марта 2010

А как насчет упаковщиков?

public class ThirdPartyClass1 {
    public string Name {
        get {
            return "ThirdPartyClass1";
        }
    }

    public void DoThirdPartyStuff() {
        Console.WriteLine("ThirdPartyClass1 is doing its thing.");
    }
}

public interface IThirdPartyClassWrapper {
    public string Name { get; }
    public void DoThirdPartyStuff();
}

public class ThirdPartyClassWrapper1 : IThirdPartyClassWrapper {

    ThirdPartyClass1 _thirdParty;

    public string Name {
        get { return _thirdParty.Name; }
    }

    public void DoThirdPartyStuff() {
        _thirdParty.DoThirdPartyStuff();
    }
}

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

1 голос
/ 19 марта 2010

Добавить интерфейс. Вы можете добавить одну оболочку (которая реализует интерфейс) для каждого из третьих сторон.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...