Вспомогательный метод апскейтинга? - PullRequest
1 голос
/ 12 января 2012

У меня есть следующие классы:

class Alpha
{
}
class Beta : Alpha
{
}
class Gamma : Beta
{
}
class Epsilon : Beta
{
}

И у меня есть пара методов, в которых я беру их в качестве параметров:

void Receiver(Gamma gamma);
void Receiver(Epsilon epsilon);
void Receiver(Beta beta);
void Receiver(Alpha alpha);

Мне здесь требуется что-то немного необычное.
Я хочу иметь возможность передавать объекты Alpha всем методам, но я не хочу, чтобы такие методы, как void Receiver(Beta beta);, могли получать объекты типов, которые наследуются от Beta (то есть я хочу, в худшем случае, повыситьException, если передан объект Gamma или Epsilon.

Как лучше всего реализовать это таким образом, чтобы он был достаточно универсальным?

Я думал о первомчасть, у меня должен быть метод, который учитывает апскейтинг, такой как

class Alpha
{
    public T Upcast<T>() where T : Alpha
    {
        // somehow return a T
    }
}

Проблемы здесь в том, что я хочу, чтобы это работало на всех уровнях, например, я хочу иметь возможность "апскейтинга" Betaтоже.

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

, поэтому чего-то вроде этого должно быть достаточно:

void Receiver(Epsilon epsilon)
{
    // if epsilon inherits from Epsilon, throw.
}

Можете ли вы помочь мне с первой частью моей проблемы? Спасибо!

Ответы [ 3 ]

2 голосов
/ 12 января 2012

Вы, похоже, боретесь с кодом, который может работать с данным типом, но нельзя доверять для правильной работы с подтипами.Это, вообще говоря, нарушение многих принципов ООП, одним из которых является LSP или Принцип замещения Лискова .

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

Ваши методы void Receiver по сути уже являются реализациями посетителей, поэтому давайте переименуем их и создадим интерфейс.

interface IVisitor
{
    void Visit(Gamma gamma);
    void Visit(Epsilon epsilon);
    void Visit(Beta beta);
    void Visit(Alpha alpha);
}

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

class Visitor : IVisitor
{
    public void Visit(Gamma gamma) { Console.WriteLine("Visiting Gamma object"); }
    public void Visit(Epsilon epsilon) { Console.WriteLine("Visiting Epsilon object"); }
    public void Visit(Beta beta) { Console.WriteLine("Visiting Beta object"); }
    public void Visit(Alpha alpha) { Console.WriteLine("Visiting Alpha object"); }
}

Затем пусть каждый тип элемента также реализует простой и общий интерфейс

interface IElement { void Accept(IVisitor visitor); }

class Alpha : IElement 
{
    public virtual void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
} 

class Beta : Alpha 
{
    public override void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
}

// etc.  

(Строго говоря, интерфейсы не необходимы , это просто хорошоform.)

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

Alpha alpha = new Beta();
IVisitor visitor = new Visitor();
alpha.Accept(visitor);

Если вы правильно его внедрили, вы должны увидеть «Посещение бета-объекта» на экране (или, по вашему мнению, вы должны увидеть желаемое поведение «Приемник»).


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


Мне требуется нечто необычное.Я хочу иметь возможность передавать объекты Alpha всем методам, но мне не нужны такие методы, как void Receiver (Beta beta);возможность получать объекты типов, которые наследуются от бета-версии (то есть, я хочу, в худшем случае, вызвать исключение, если передан объект Gamma или Epsilon.

Это действительно очень странно. ИЯ не уверен, что вы на самом деле этого хотите. У вас, кажется, иерархия наследования перевернута с ног на голову, где супертипы могут быть заменены производными типами, а не наоборот, и вы должны действительно пересмотретьВы пытаетесь сделать.

0 голосов
/ 12 января 2012

Я хочу иметь возможность передавать объекты Alpha всем методам

Вы не можете сделать это надежно. Альфа не является гаммой, поэтому вы не можете надежно использовать ее в качестве параметра для приемника (гамма). В противном случае вы можете сделать это:

Alpha a = new Gamma(); // legal
Receiver((Epsilon)a);    // not legal

Если вы определили функции Receiver, как и в своем вопросе, компилятор выберет правильный метод на основе объявленного типа (или приведенного типа, если вы его приведете):

Epsilon e = new Epsilon();
Alpha a = new Gamma();

Receiver(e);         // calls Receiver(Epsilon)
Receiver(a);         // calls Receiver(Alpha)
Receiver((Gamma)a);  // calls Receiver(Gamma)
Receiver((Beta)e);   // calls Receiver(Beta)

Таким образом, компилятор выберет правильный метод, если вы вызовете его правильно.

но я не хочу, чтобы такие методы, как void Receiver(Beta beta);, могли получать объекты типов, которые наследуются от бета-версии

Тогда вы не правильно используете наследование. Gamma является Beta. Почему вы хотите предотвратить использование метода наследуемыми типами? Если вы хотите, чтобы Gamma и Beta совместно использовали некоторые свойства / методы без наследования, выберите другой шаблон, например Encapsulation или Composition и используйте интерфейсы.

Тем не менее, ваша функция Upcast должна быть простой:

public T Upcast<T>() where T : Alpha
{
    return this as T;  // will return null is this is not a T
}

...

new Beta().Upcast<Gamma>();  // will return null
new Epsilon().Upcate<Epsilon>();  // will return the Epsilon
0 голосов
/ 12 января 2012

Вы можете сделать GetType и проверить, что это бета.

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