Подтвердить что ты не робот

Как называется эта плохая практика/анти-шаблон?

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

public void ControlStuff()
    {
        var listOfThings = LoadThings();
        var listOfThingsThatSupportX = new string[] {"ThingA","ThingB", "ThingC"};
        foreach (var thing in listOfThings)
        {
            if(listOfThingsThatSupportX.Contains(thing.Name))
            {
                DoSomething();
            }
        }
    }

Я предлагаю добавить свойство к базовому классу "Вещи", чтобы сообщить нам, поддерживает ли он X, поскольку подкласс Thing должен будет реализовать эту функциональность. Что-то вроде этого:

public void ControlStuff()
    {
        var listOfThings = LoadThings();
        foreach (var thing in listOfThings)
        {
            if (thing.SupportsX)
            {
                DoSomething();
            }
        }
    }
class ThingBase
{
    public virtual bool SupportsX { get { return false; } }
}
class ThingA : ThingBase
{
    public override bool SupportsX { get { return true; } }
}
class ThingB : ThingBase
{
}

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

4b9b3361

Ответ 1

Обычно лучшим подходом (IMHO) было бы использование интерфейсов вместо наследования

то это просто вопрос проверки того, реализовал ли объект интерфейс или нет.

Ответ 2

Я думаю, что имя анти-шаблона жесткое кодирование:)

Должно ли быть ThingBase.supportsX, по крайней мере, немного зависит от того, что X. В редких случаях это знание может быть только в ControlStuff().

Более обычно, X может быть одним из множества вещей, в этом случае ThingBase может потребоваться раскрывать свои возможности с помощью ThingBase.supports(ThingBaseProperty) или некоторых таких.

Ответ 3

IMO основополагающим принципом проектирования здесь является инкапсуляция. В предлагаемом решении вы инкапсулировали логику внутри класса Thing, где, как и в исходном коде, логика просачивается в вызывающих абонентов.

Он также нарушает принцип Open-Closed, так как если вы хотите добавить новые подклассы, поддерживающие X, теперь вам нужно идти и изменять в любом месте, содержащем этот жесткий список. С вашим решением вы просто добавляете новый класс, переопределяете метод и все готово.

Ответ 4

Не знаю о имени (сомнение такое существует), но думайте о каждой "Вещи" как о машине - некоторые автомобили имеют систему круиз-контроля, а другие нет.

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

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

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

Не очень техническое объяснение, но простое и точное.

Ответ 5

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

Ответ 6

Слишком много написания кода. Это затрудняет чтение и понимание.

Как уже указывалось, было бы лучше использовать интерфейс.

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

Ответ 7

Это просто плохой код, у него нет имени для него (у него даже нет дизайна OO). Но аргумент может заключаться в том, что первый код не подпадает под принцип открытого закрытия. Что происходит, когда меняется список поддерживаемых вещей? Вы должны переписать метод, который используете.

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

Ответ 8

Я не думаю, что у него есть имя, но, возможно, проверьте главный список http://en.wikipedia.org/wiki/Anti-pattern знает? http://en.wikipedia.org/wiki/Hard_code, вероятно, выглядит ближе.

Я думаю, что ваш пример, вероятно, не имеет имени, тогда как ваше предлагаемое решение называется Композитный.

http://www.dofactory.com/Patterns/PatternComposite.aspx

Ответ 9

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

// invoked to map different kinds of items to different features
public void BootStrap
{
    featureService.Register(typeof(MyItem), new CustomFeature());
}

// your code without any ifs.
public void ControlStuff()
{
    var listOfThings = LoadThings();
    foreach (var thing in listOfThings)
    {
        thing.InvokeFeatures();
    }
}

// your object
interface IItem
{
    public ICollection<IFeature> Features {get;set;}

    public void InvokeFeatues()
    {
        foreach (var feature in Features)
            feature.Invoke(this);
    }
}

// a feature that can be invoked on an item
interface IFeature
{
    void Invoke(IItem container);
}

// the "glue"
public class FeatureService
{

    void Register(Type itemType, IFeature feature)
    {
        _features.Add(itemType, feature);
    }

    void ApplyFeatures<T>(T item) where T : IItem
    {
        item.Features = _features.FindFor(typof(T));
    }
}

Ответ 10

Я бы назвал его Failure to Encapsulate. Это составленный термин, но он реален и довольно часто встречается

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

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

Я бы предложил "улучшенное" решение, которое должно иметь список объектов thing, с методом, реализующим DoSomething(), который действует как NOOP для вещей, которые ничего не делают. Это локализует поведение thing внутри себя, а обслуживание специального списка соответствия становится ненужным.

Ответ 11

Если бы это была одна строка, я мог бы назвать ее "магической строкой". В этом случае я бы рассмотрел "магический массив строк".

Ответ 12

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

Ответ 13

Для меня лучше всего объяснить это с точки зрения вычислительной сложности. Нарисуйте две диаграммы, показывающие количество операций, требуемых в терминах count(listOfThingsThatSupportX ) и count(listOfThings ), и сравните их с предлагаемым решением.

Ответ 14

Вместо использования интерфейсов вы можете использовать атрибуты. Вероятно, они будут описывать, что объект должен быть помечен как объект такого типа, даже если его пометка как таковая не вводит никаких дополнительных функций. То есть объект, описываемый как "Thing A" , не означает, что все "Thing A" имеют определенный интерфейс, просто важно, чтобы они были "Thing A" . Это похоже на работу атрибутов больше, чем на интерфейсы.