Проблема проектирования с интерфейсами C # - PullRequest
2 голосов
/ 27 июля 2011

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

#region Library A 
// This region represents a library (A) defining some interfaces

/// <summary>
/// Interface to be implemented by any processor of type A
/// </summary>
interface AProcessor
{
    void Process(AProcessable a);
}

/// <summary>
/// Interface to be implemented by any object processable by a processor of type A
/// </summary>
interface AProcessable
{
    int MyPropertyA { get; set; }
}
#endregion


#region Library B
// This region represents a library (B) defining some other interfaces

/// <summary>
/// Interface to be implemented by any processor of type B
/// </summary>
interface BProcessor
{
    void Process(BProcessable a);
}

/// <summary>
/// Interface to be implemented by any object processable by a processor of type B
/// </summary>
interface BProcessable
{
    int MyPropertyB { get; set; }
}
#endregion

Затем в моем приложении я реализую тип расходных материалов, который реализует оба интерфейса расходных материалов из каждой библиотеки:

/// <summary>
/// A type processable by processors of type A and processors of type B.
/// </summary>
class Processable :
    AProcessable,
    BProcessable
{
    // Implements interface AProcessable
    public int MyPropertyA { get; set; }

    // Implements interface BProcessable
    public int MyPropertyB { get; set; }
}

И я реализую потребительский тип, способный потреблять объекты типа Processable, который реализует AProcessable и BProcessable:

/// <summary>
/// A processor that implements an A-type processor and a B-type processor.
/// </summary>
class Processor : 
    AProcessor, 
    BProcessor
{
    // I thought that Process method would implement AProcessor.Process() and
    // BProcessor.Process() as it expects a Processable object as parameter which
    // implements AProcessable and BProcessable, but it doesn't.
    // Compiler rises an error telling that Processor doesn't implement neither
    // AProcessor.Process() nor BProcessor.Process()
    /*
    public void Process(Processable a)
    {
        throw new NotImplementedException();
    }
    */


    // Implementing both methods does not work because it creates ambiguity: when
    // we call Processor.Process(p) being "p" a Processable object the compiler 
    // doesn't know if it has to call Processor.Process(AProcessable) or
    // Processor.Process(BProcessable).
    /*
    public void Process(AProcessable a)
    {
        throw new NotImplementedException();
    }

    public void Process(BProcessable a)
    {
        throw new NotImplementedException();
    }
    */
}

¿В чем моя ошибка в дизайне и каков будет правильный подход?

Заранее спасибо.

Ответы [ 5 ]

4 голосов
/ 27 июля 2011

Вы ищете реализации явного интерфейса.Эта ссылка имеет учебное пособие:

http://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx

По сути, сигнатура метода показывает тип интерфейса:

void BProcessor.Process(BProcessable b)
{

}

и доступна только при прямой ссылке наBProcessor:

BProcessor b;
b.Process(null);

Выводимая ссылка на BProcessor не будет работать с явными реализациями:

Processor p;
p.Process(new BProcessable()); // This won't compile.

Это позволяет реализовывать интерфейсы с конфликтующими именами членов.Если у них есть конфликтующие имена типов, вам нужно начать искать конфликты пространства имен.

Вы заметите явную реализацию, потому что когда вы получите Intellisense для Processor, он будет отсутствовать Process(BProcessable b).

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

public void Process(Processable p)
{

}

void AProcess.Process(AProcessable a)
{
    Process((Processable)a);
}

void BProcess.Process(BProcessable b)
{
    Process((Processable)b);
}

Однако лично я бы избегал этого типа прямого перенаправления, поскольку вы не гарантированы (кромеВы полностью контролируете код), чтобы получить тип Processable.

2 голосов
/ 27 июля 2011

Используйте явные реализации интерфейса:

void AProcessor.Process(AProcessable a)
{
    throw new NotImplementedException();
}

void BProcessor.Process(BProcessable a)
{
    throw new NotImplementedException();
}

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

Processor p;
Processable pa;
((AProcess)p).Process(pa);

За исключением обработки этой ситуации с конфликтующими именами / сигнатурами методов, явные реализации также могут использоваться для сокрытия (или, по крайней мере, усложнения егоиспользовать) методы, которые необходимы интерфейсу, но не имеют смысла для определенного класса.

1 голос
/ 27 июля 2011

Я не думаю, что здесь требуется явная реализация интерфейса, поскольку нет конфликта между сигнатурами двух методов обработки (тип аргумента AProcessable для AProcessor и BProcessable для второго).Поскольку я считаю, что для Processable экземпляров вы хотите, чтобы обе обработки выполнялись, я бы реализовал это следующим образом:

public class Processor : AProcessor, BProcessor
{
    #region AProcessor Members

    public void Process(AProcessable a)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    #endregion

    #region BProcessor Members

    public void Process(BProcessable a)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    #endregion

    public void Process(Processable a)
    {
        Process((AProcessable)a);
        Process((BProcessable)a);
    }
}
0 голосов
/ 27 июля 2011

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

По сути, вы пытаетесь смешать два совершенно разных понятия в один логический управляющий фрагмент кода (класс Processor).

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

Что касается другого решения о том, как «исправить» проблему (я использую слово «исправить» слегка, поскольку трудно предложить альтернативное решение, не зная контекста решаемой вами проблемы), вам действительно нужно найти способ представления двух обрабатываемых типов.и процессоры обычным способом .... или просто нетсмешайте две концепции в конкретной реализации Процессора.

Знание большего о реальном контексте проблемы поможет нам дать более точное предложение!:)

Удачи!

0 голосов
/ 27 июля 2011

В вашей первой попытке с помощью одного метода вы не реализуете точное определение из интерфейса, которому не нужен объект Processable в качестве параметра, а объект AProcessable и BProcessable, в зависимости от того, какой вы хотите.

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

Если вы хотите вызвать их обоих, то вынужно сделать два отдельных вызова - вы можете реализовать метод Process с помощью класса и вызвать AProcessable.Process и BProcessable.Process.Но это должно быть сделано явно, а не только через наследование и интерфейсы.

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