Разделяет на подклассы в Objective C плохую практику?

//C++ 11 SFINAE fold
template<size_t I = 0, typename F, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type       //I == end
    for_each(std::tuple<Ts...>&, const F&) {}


template<size_t I = 0, typename F, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type        //I < end
    for_each(std::tuple<Ts...>& tuple, const F& function)
{
    function(std::get<I>(tuple));
    for_each<I + 1>(tuple, function);
}

//C++ 17
template<size_t I = 0, typename F, typename ...Ts>
void for_each(std::tuple<Ts...>& tuple, const F& function)
{
    if constexpr(I < sizeof...(Ts))
    {
        function(std::get<I>(tuple));
        for_each<I + 1>(tuple, function);
    }
}

//Client
for_each(std::tie(array1, array2, array3, array4, array5, array6, array7, array8, array9), 
    [](auto& array)
    { 
        for(auto* element : array)
        {
           element = new A();
           element->foo();
        }
    });
17
задан 9 revs 12 May 2009 в 12:39
поделиться

7 ответов

Лично я следую этому правилу: я могу создать подкласс, если он соблюдает Принцип подстановки Лискова .

10
ответ дан 30 November 2019 в 10:50
поделиться

Subclassing has it's benefits, but it also has some drawbacks. As a general rule, I try to avoid implementation inheritance and instead use interface inheritance and delegation.

One of the reasons I do this is because when you inherit implementation, you can wind up with problems if you override methods but don't adhere to their (sometimes undocumented contract). Additionally, I find walking class hierarchies with implementation inheritance difficult because methods can be overridden or implemented at any level. Finally, when subclassing you can only widen an interface, you can't narrow it. This leads to leaky abstractions. A good example of this is java.util.Stack which extends java.util.Vector. I shouldn't be able to treat a stack as a Vector. Doing so only allows the consumer to run around the interface.

Others have mentioned the Liskov Substitution Principle. I think that using that would have certainly cleared up the java.util.Stack problem but it can also lead to very deep class hierarchies in order to put ensure that classes get only the methods they are supposed to have.

Instead, with interface inheritance there is essentially no class hierarchy because interfaces rarely need to extend one another. The classes simply implement the interfaces that they need to and can therefore be treated in the correct way by the consumer. Additionally, because there is no implementation inheritance, consumers of these classes won't infer their behavior due to previous experience with a parent class.

In the end though, it doesn't really matter which way you go. Both are perfectly acceptable. It's really more a matter of what you're more comfortable with and what the frameworks that you're working with encourage. As the old saying goes: "When in Rome do as Romans do."

7
ответ дан 30 November 2019 в 10:50
поделиться

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

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

4
ответ дан 30 November 2019 в 10:50
поделиться

Нет ничего плохого в использовании наследования в Objective-C. Apple довольно часто этим пользуется. Например, в Cocoa-touch деревом наследования UIButton является UIControl: UIView: UIResponder: NSObject.

Я думаю, что Мартин затронул важный момент, упомянув принцип подстановки Лискова. Кроме того, правильное использование наследования требует, чтобы разработчик подкласса имел глубокие знания суперкласса. Если вы когда-либо пытались расширить нетривиальный класс в сложной структуре, вы знаете, что всегда есть кривая обучения. Кроме того, детали реализации суперкласса часто «просачиваются» в подкласс, что является большой проблемой @ $ & для разработчиков инфраструктуры.

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

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

Я часто вижу, как программисты приложений извлекают уроки из дизайна фреймворков и пытаются применить их к своему коду приложения (это распространено в мирах Java, C ++ и Python, а также в Objective-C). Хотя хорошо подумать и понять выбор, сделанный разработчиками фреймворка, эти уроки не всегда применимы к коду приложения.

удобнее всего работать с вашим дизайном. Наследование - отличный инструмент при правильном использовании.

Я часто вижу, как программисты приложений извлекают уроки из дизайна фреймворков и пытаются применить их к своему коду приложения (это распространено в мирах Java, C ++ и Python, а также в Objective-C). Хотя хорошо подумать и понять выбор, сделанный разработчиками фреймворка, эти уроки не всегда применимы к коду приложения.

удобнее всего работать с вашим дизайном. Наследование - отличный инструмент при правильном использовании.

Я часто вижу, как программисты приложений извлекают уроки из дизайна фреймворков и пытаются применить их к своему коду приложения (это распространено в мирах Java, C ++ и Python, а также в Objective-C). Хотя хорошо подумать и понять выбор, сделанный разработчиками фреймворка, эти уроки не всегда применимы к коду приложения.

6
ответ дан 30 November 2019 в 10:50
поделиться

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

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

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

Аналогичные концепции относятся к делегатам и протоколам. Если вы можете вписать свои идеи в классы Apple, то, как правило, проще (и будет работать более плавно) сделать это и использовать источник данных, делегаты и протоколы, чем создавать свои собственные подклассы. Это помогает избежать лишней работы и потери времени и обычно менее подвержено ошибкам. Классы Apple позаботились о том, чтобы сделать функции эффективными и отладить; чем больше вы сможете с ними работать, тем меньше ошибок будет в вашей программе в конечном итоге.

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

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

3
ответ дан 30 November 2019 в 10:50
поделиться

Мое впечатление о том, что ADC делает упор «против» подклассов, больше связано с наследием того, как развивалась операционная система ... в те времена (Mac Classic aka os9), когда C ++ был Основным интерфейсом для большинства инструментов Mac, создание подклассов было стандартом де-факто, чтобы программист мог изменить поведение обычных функций ОС (и это действительно иногда было головной болью и означало, что нужно было быть очень осторожным, чтобы любые модификации вели себя предсказуемо и не нарушали стандартное поведение).

при этом, МОЕ ВПЕЧАТЛЕНИЕ акцента ADC против создания подклассов не выдвигает аргументы в пользу проектирования иерархии классов приложения без наследования, НО ВМЕСТО , чтобы указать, что в новом способе работы (т.е. OSX) в большинстве случаев есть более подходящие средства для настройки стандартного поведения без необходимости создания подклассов.

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

с нетерпением жду вашей новой классной головоломки!

| K <

2
ответ дан 30 November 2019 в 10:50
поделиться

Delegation is a means of using the composition technique to replace some aspects of coding you would otherwise subclass for. As such, it boils down to the age old question of the task at hand needing one large thing that knows how to do a lot, or if you have a loose network of specialized objects (a very UNIX sort of model of responsibility).

Using a combination of delegates and protocols (to define what the delegates are supposed to be able to do) provides a great deal of flexibility of behavior and ease of coding - going back to that Liskov substitution principle, when you subclass you have to be careful you don't do anything a user of the whole class would find unexpected. But if you are simply making a delegate object then you have much less to be responsible for, only that the delegate methods you implement do what that one protocol calls for, beyond that you don't care.

There are still many good reasons to use subclasses, if you truly have shared behavior and variables between a number of classes it may make a lot of sense to subclass. But if you can take advantage of the delegate concept you'll often make your classes easier to extend or use in ways you the designer may not have expected.

I tend to be more of a fan of formal protocols than informal ones, because not only do formal protocols make sure you have the methods a class treating you as a delegate expect, but also because the protocol definition is a natural place to document what you expect from a delegate that implements those methods.

12
ответ дан 30 November 2019 в 10:50
поделиться