2011-12-28 2 views
3

У меня есть другой класс фрукты, все реализует тот же интерфейс IFruit:Как переработать методы вызова, которые выглядят одинаково?

public interface IApple : IFruit{ } 
public interface IBanana : IFruit{ } 
public interface ICarrot: IFruit{ } 

Каждый из них имеет свой собственный ящик:

public class AppleDrawer 
{ 
    public void Draw(IApple apple, Graphics graphics){} 
} 

public class BananaDrawer 
{ 
    public void Draw(IBanana banana, Graphics graphics){} 
} 

Если я хочу нарисовать список фруктов, я делаю следующие

public void DrawFruits(List<IFruit> fruits, Graphics graphics) 
{ 
    foreach(var fruit in fruits) 
    { 
     if(fruit is IBanana) 
     { 
      var banana = (IBanana)fruit; 
      var drawer = new BananaDrawer(); 
      drawer.Draw(banana, graphics); 
     } 
     else if(fruit is IApple) 
     { 
      var apple = (IApple)fruit; 
      var drawer = new AppleDrawer(); 
      drawer.Draw(banana, graphics); 
     } 
     etc... 

} 

Я чувствую себя очень грязным, когда читаю свой код.
Моя проблема заключается в множественном утверждении if..else, потому что у меня есть 12 разных плодов, и я должен сделать это заявление много в моем текущем проекте.

Есть ли способ рефакторинга моего метода DrawFruits?
Я думаю о какой-то фабричной схеме, но я действительно не вижу, как это сделать.
Является ли мой класс фруктов брать ящик как собственность? Или, может быть, я могу назвать метод Drawer Factory?

Это образец, который я нахожу в моем текущем проекте, и я не нахожу решение, которое меня удовлетворяет.

+2

Почему бы просто не использовать 'IFruitDrawer'? –

ответ

5

Один из способов иметь GetDrawer на вашем IFruit

public interface IFruit 
{ 
    BaseDrawer GetDrawer(); 
} 

и интерфейс BaseDrawer

public interface BaseDrawer 
{ 
    void Draw(IFruit fruit, Graphics graphics); 
}. 

public class AppleDrawer : BaseDrawer 
{ 
    public void Draw(IFruit apple, Graphics graphics) { } 
} 

public class BananaDrawer : BaseDrawer 
{ 
    public void Draw(IFruit banana, Graphics graphics) { } 
} 

Теперь ваши ничья Фрукты просто

public void DrawFruits(List<IFruit> fruits, Graphics graphics) 
    { 
     foreach (var fruit in fruits) 
     { 
      var drawer = fruit.GetDrawer(); 
      drawer.Draw(fruit, graphics); 
     } 
    } 

Иногда вам нужно Drawer , Plotter и Printer поэтому ваш IFruit может получить слишком тяжелый, как ниже

public interface IFruit 
{ 
    BaseDrawer GetDrawer(); 
    BasePrinter GetPrinter(); 
    BasePlotter GetPlotter(); 
} 

Visitor pattern is a good solution for this. В основном вы будете иметь

public interface iFruit 
    { 
     void Accept(FruitVisitor visitor); 
    } 

только один класс для всех возможных посещений рисования

public class DrawVisitor : FruitVisitor 
    { 
     public override void Visit(Apple apple) 
     { 
     //draw the apple 
     } 

     public override void Visit(Banana banana) 
     { 
     // draw the banana 
     } 
    } 

Здесь вы просто один DrawVisitor вместо AppleDrawer, BananaDrawer и т.д., и весь ваш код ничейный аккуратно в одном месте. Возможно, вам понадобится PlotterVisitor, PrinterVisiter и т. Д.

+0

Вместо того, чтобы иметь GetDrawer, вы можете использовать метод Draw. –

+1

@SaeedAmiri Да ...put, если OP отделил код Draw от IFruit, я предполагаю, что он не хочет реализации кода рисования в ** Apple, Banana и т. д. и т. д. и т. д. ** конкретный класс, но в суррогатном классе –

+0

Во всем, что вы использовали Drawer в своем фрукты, он не изменяет, если вы вызываете GetDrawer или вызываете Drawer.Draw, на самом деле оба они имеют одинаковые зависимости. но ваш выбор затрудняет вызов кода. (просто добавляет лишнюю работу больше ничего). –

4

Возможно, вы можете сделать абстрактный класс Fruit с помощью метода Draw и абстрактного класса FruitDrawer соответственно.

Например:


public abstract class Fruit { 
    ... 
} 

public abstract class FruitDrawer { 
    public void Draw(Fruit f, Graphics g) 
    { 
    ... 
    } 
} 
+0

Как я тогда назвал правильный ящик? –

+0

'FruitDrawer.Draw' будет иметь общие элементы любых фруктов, которые будут нарисованы. Вы можете использовать шаблон @parapura rajkumar. –

+0

Таким образом, у вас будет класс if-then-else в классе FruitDrawer, ничего не изменится. –

2

Вы в основном регургитации канонический пример нарушения Open-Closed Principle, но с фруктами, а не формы.

Можно было бы погрузиться во все ваши вопросы, но если вы изучите SOLID Principles, вы узнаете гораздо больше.

По просьбе я погружусь в ваш код.

Прежде всего, я бы нажал Draw на интерфейс IFruit, чтобы каждый плод отвечал за рисование себя, а не за класс контроллера, манипулирующий всем. Вы могли в конечном итоге с единоличной ответственностью (S в SOLID), но это был бы другой рефакторинг. Важным здесь является то, что плоды рисовать себя и контроллер открыт для расширения (добавляя больше фруктов классов), но закрыт для модификаций (так как они рисуют себя, контроллер никогда не меняется).

Затем вы получите простой цикл, рисующий ваши плоды.

foreach(var fruit in fruits) 
    fruit.Draw(...); 

Для решения «как не [пауза] их» комментарий ...

Зная, что они это первый шаг к карьере обучения и их применения. Самый простой способ избежать их (и это не совсем просто) - это быть очень строгим в отношении разработки, основанной на тестах. Многое из того, что вы дали в качестве примеров, было бы очень трудно осознать с помощью TDD.Другими словами, если вы знаете принципы SOLID и, делающие TDD, вы не получили бы код, который вы опубликовали.

+2

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

+0

Правда, потому что я читал «Твердые принципы», и это потому, что я читал их, что знаю, что я их сломал. Но я до сих пор не знаю, КАК их не сломать! –

+0

@CodyGray: Отредактировано по вашему запросу. –

1

Я думаю, что лучше делать так:

interface IFruit 
{ 
    void Draw(); 
} 

class Banana : IFruit 
{ 
    void Draw() 
    { 
     BananaDrawer drawer = new BananaDrawer(); 
     drawer.Draw(); 
    } 
} 

и использовать его просто:

foreach(var fruit in fruits) 
    fruit.Draw(); 

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

+0

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

+0

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

Смежные вопросы