Какой шаблон проектирования использовать при рефакторинге двух классов, содержащих наполовину связанные и наполовину несвязанные функции? - PullRequest
0 голосов
/ 08 февраля 2011

У меня есть два объекта: Птица и Собака . Птица и Собака имеют идентичные реализации для примерно 50% методов / свойств, но разные несвязанные методы и свойства для остальных 50%.

Я занимаюсь рефакторингом и не знаю, какая стратегия самая лучшая.

Я попытался определить суперкласс Animal , чтобы реализовать общие методы в суперклассе и позволить дочерним классам определять свои собственные методы / свойства.

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

public class Animal{
   public string Talk(){ return "yak yak yak";
}
public class Dog:Animal{
   public string Walk(){ return "walk walk walk"; }
}
public class Bird:Animal{
   public string Fly(){ return "flap flap flap"; }
}
...
Animal thing = CreatureFactory.GetCreature(modifier);

Когда я хочу использовать вещь до Talk нет проблем,

Debug.Print(thing.Talk());

но как насчет того, когда я, как программист, хочу, чтобы он был Fly , могу ли я привести его к Bird ? Это кажется неправильным ... но определение Fly метода для Dog тоже кажется неправильным.

Ответы [ 5 ]

3 голосов
/ 08 февраля 2011

Вы либо бросаете это птице:

Debug.Print(((Bird)thing).Fly());

или вы все время относитесь к ней как к птице:

// depending on how the factory works, might not need the cast
Bird thing = (Bird) CreatureFactory.GetCreature(modifier);
Debug.Print(bird.Fly());
2 голосов
/ 08 февраля 2011

В вашем примере, когда вы хотите, чтобы ваш Animal летал, класс, выполняющий полет, работает с неправильным классом - он должен использовать сам Bird или какой-то ICanFly интерфейс.

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

2 голосов
/ 08 февраля 2011

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

Dog dog = CreatureFactory.GetDog();

Теперь вы все еще можете использовать их как Animal.

public class Trainer
{
     public void TeachToSpeak( Animal animal )
     {
          ...
          animal.Talk();
     }
}

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

1 голос
/ 08 февраля 2011

С одной стороны, я бы поместил виртуальный метод в базовый класс с именем Move() и переопределил его в производных классах.

(C #)

public abstract class Animal {
   public string Talk() { return "yak yak yak"; }
   public virtual string Move();
}
public class Dog : Animal {
   public override string Move() { return "walk walk walk"; }
}
public class Bird : Animal {
   public override string Move() { return "flap flap flap"; }
}

Но чтобы ответить на ваш вопрос, если вы хотите, чтобы животное двигалось, если (и только если) оно может летать, вы можете определить интерфейс IFlyingAnimal и реализовать его с помощью Bird. Затем вы можете проверить, реализует ли Animal этот интерфейс. Если это так, приведите его к IFlyingAnimal и вызовите его метод Fly().

public interface IFlyingAnimal {
   string Fly();
}
public class Bird : Animal, IFlyingAnimal {
   public string Fly(){ return "flap flap flap"; }
}

//later, in your main program
public string FlyIfYouCan(Animal animal) {
    if (animal is IFlyingAnimal)
        return ((IFlyingAnimal)animal).Fly();

    return "I can't fly!";
}

У вас нет для использования интерфейса; Вы можете просто использовать if (animal is Bird) вместо этого. Но гораздо лучше практиковать это так; птицы - не единственные животные, которые могут летать, поэтому вы принимаете решение, основываясь на том, что ваш элемент делает , а не на том, что составляет . Вот для чего нужны интерфейсы.

1 голос
/ 08 февраля 2011

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

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