Что такое хороший способ настроить форму, которая поддерживает несколько объектов с одинаковым базовым типом? - PullRequest
1 голос
/ 15 марта 2012

У меня есть базовый тип (A), который имеет два производных (B и C).Базовый тип не является абстрактным.Итак, у меня есть три объекта.

Единственная разница между B и C состоит в том, что они оба имеют одно дополнительное свойство:

  • B.Foo
  • C.Бар

Теперь у меня есть такие условия в моем коде:

if(myObject is B)
     myDatabindB.DataSource = ((B)myReport).Foo);
else if(myObject is C)
     myDatabindC.DataSource = ((C)myReport).Bar);

и в другом методе:

pnlSomePanel.Visible = myObject is B;
pnlSomeOtherPanel.Visible = myObject is C;

Но вы можете себе это представить, когда естьновый тип, я должен обновить все мои операторы if-else.Это нарушает многие принципы ОО.

Но проблема в том, что я не могу придумать хорошее и чистое решение для решения этой проблемы.У вас есть предложение / идея для решения этой проблемы?

РЕДАКТИРОВАТЬ: Если это имеет значение, я использую шаблон MVP.

Ответы [ 4 ]

2 голосов
/ 15 марта 2012

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

Большая цель здесь состоит в том, чтобы увеличить инкапсуляцию классов A, B и C - чтобы убедиться, что все, что относится к A, B или C, хранится в этих классах ине перемещаться, скажем, в операторы if в другом месте.

Мы можем переместить логику для определения того, что является правильным источником данных из контроллера (который выполняет привязку) к вашему отчету.Имя этого метода должно быть описательным, как GetReportSubjectLine ().

class A{
    <snip>
    public virtual SomeDataType getDataSourceForViewType(){
     throw new NotImplementedException()
    }
}

class B{
<snip>
public override SomeDataType getDataSourceForViewType(){
    return this.Foo;
    }
}

class C{
 public override SomeDataType getDataSourceForViewType(){
    return this.Bar;
    }
}

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

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

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

public class PanelVisibilityManager{
    ICollection<Panel> ManagedPanels {get; set;}
    //
    public IDictionary<System.Type, ICollection<Panel>> Switchboard {get; set;}

    public void TogglePanelsFor(System.Type item){
        foreach(var panel in ManagedPanels){
            panel.Visible=false;
            }
        foreach(var panel in Switchboard[item]){
                 panel.Visible=true;              
            }

}

Надеюсь, это поможет!

0 голосов
/ 16 марта 2012

Как насчет Dictionary<Type, Action>?

Тогда вы могли бы сделать что-то вроде этого:

var myActors = new Dictionary<Type, Action<BaseClass>>();

myActors.Add(typeof(classA), DoSomethingWithA);
myActors.Add(typeof(classB), DoSomethingWithB);

...

Action actor;

if(myActors.TryGetValue(specialRetrievedOnlyAsBase.GetType(), actor))
{
    ResetEverything();
    actor(specialRetrievedOnlyAsBase);
}
else
{
    // ToDo: What should happen if this type is not supported?
}

...

private void DoSomethingWithA(BaseClass)
{
    var classAObject = (ClassA)BaseClass;

    // ToDo: What should happen if classA arrives?
}

private void DoSomethingWithA(BaseClass)
{
    var classAObject = (ClassB)BaseClass;

    // ToDo: What should happen if classB arrives?
}
0 голосов
/ 15 марта 2012

Шаблон стратегии очень хорошо подходит для первого случая

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

0 голосов
/ 15 марта 2012

Один из способов избежать такого типа кода - перенести ответственность принятия решений в сам объект.Например:

Определить некоторую коллекцию A.

List<A> objects = new List<A>{new B(), new C()}

Вместо if/else использовать foreach over collection и вызывать для каждого объекта виртуальный методопределенный в A и переопределяемый в childs, например

virtual bool ThisIsMe(A objectToCheck){}

B и C переопределяют этот метод, проверяя, является ли objectToCheck их тип, и возвращают true или false в отношениииз него.

РЕДАКТИРОВАТЬ

Пример:

public class A 
{
   public virtual bool ThisIsMe(A objectToCheck){}
   public virtual object GetData{}
}

public class B : A 
{
   public override  bool ThisIsMe(A objectToCheck)
   {   
      return objectToCheck is B;
   }

   public override object GetData()
   {
      return this.Foo;
   }
}

public class C : A 
{
   public override  bool ThisIsMe(A objectToCheck)
   {   
      return objectToCheck is B;
   }

   public override object GetData()
   {
      return this.Bar;
   }
}

Теперь вместо этого if/else, что-то вроде этого:

foreach(A objA in objects) { if(objA.ThisIsMe(myObject)) { myDatabindB.DataSource = objA.GetData(); break; } }

Можно также заменить это на какую-нибудь необычную LINQ инструкцию.

Надеюсь, это поможет.

...