Задача c — вопрос о передовом опыте ООП

Я новичок в Objective C.


Предположим, что есть 3 класса: Шеф, Еда, Блюда
Как и следовало ожидать: Шеф-повар готовит еду -> Блюдо.

- (Dishe *)cook:(Food *)food;

Теперь я хочу добавить классы ChefFish, Fish и FishDishe.
Как и следовало ожидать: Когда Шеф приготовит рыбу, у нас будет Рыбное Блюдо.


Естественно, я бы пошел на:

- (FishDishe *)fishTraitment:(Fish *)fish {
      return [FishDishe alloc];
}

- (Dishe *)cook:(Food *)food {
      if ([food isKindOfClass:[Fish class]]) {
           return [self fishTraitement:food];
      } else {
           return [Dishe alloc];
      }
}

то я получил это предупреждение: Incompatible pointer types sending 'Food *' to parameter of type 'Fish *' Конечно, сам код компилируется и запускается как положено.
Предупреждение не является реальным вопросом, так как этого можно избежать с помощью кастинга или просто переместить блок fishTraitment в методе cook.

Но покопавшись, я нашел какую-то тему с надписью использование isKindOfClass не является «чистым». Это нарушило бы принципы полиморфизма и объектной ориентации.


Мой вопрос здесь:

Что здесь будет лучшая практика?

Обратите внимание, что я мог бы иметь в другом месте массив Chef с общим Chef и ChefFish.
я бы ожидал:

Chef cook fish -> Dishe
ChefFish cook fish -> FishDishe 

Также, хотя о перегрузке, похоже, у нас не может быть метода с тем же именем и одинаковым количеством параметров (событие с другим типом и именем), что возможно в Swift :(

Отвечает ли это на ваш вопрос? Как преобразовать объект в Objective-C

Raphael Schweikert 18.03.2022 12:53

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

Fernand 18.03.2022 13:04

Благодарю за разъяснение. В этом случае вопрос на самом деле не зависит от языка. isKindOfClass похож на instanceof на других языках. Он нарушает принципы ООП лишь постольку, поскольку часто используется в тех случаях, когда полиморфизм является лучшим решением. Если бы еда знала, как приготовить из себя блюдо, можно было бы призвать [food cook] все виды еды, чтобы получить блюдо, но оно было бы менее чистым из-за отсутствия повара.

Raphael Schweikert 18.03.2022 13:24

Так что я думаю, что я пытаюсь сказать: все это компромисс. И абсолютные заявления вроде «использование isKindOfClass всегда нарушает принципы ООП» ни к чему нас не приводят. Если ваш isKindOfClass работает на вас и нет явного повторения, оставьте его. Как только это становится бременем, реорганизуйте его.

Raphael Schweikert 18.03.2022 13:28

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

Raphael Schweikert 18.03.2022 13:32

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

Raphael Schweikert 18.03.2022 13:36

Спасибо за все эти объяснения, такому новичку, как я, действительно сложно принять решение, поэтому даже если ответы основаны на мнении, это всегда полезно, особенно когда я не уверен в языке. Ваш комментарий - это именно то, чего я жду, я бы принял ответ, если бы вы могли перегруппировать все эти комментарии в один. (я знаю, что это не на 100% связано с языком, поэтому я пометил его тегом oop)

Fernand 18.03.2022 14:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
7
33
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В Objective-C нет такой перегрузки, как в C++.

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

Все объекты Objective-C обычно в какой-то момент наследуются от NSObject. Таким образом, все они наследуют некоторые методы по умолчанию. Например: alloc, init, new (вызов alloc+init сразу), dealloc.

@interface Cook : NSObject
-(instancetype)init; //default anyway..
@end

@interface Fish : NSObject
-(instancetype)initWithCook:(Cook*)cook; //not default
// so Fish has also one 'init' method even if not declared explicit.
@end

@interface Dish : NSObject
-(instancetype)initWithCook:(Cook*)cook;
-(instancetype)initWithCook:(Cook*)cook AndFish:(Fish*)fish;
@end

@interface SmellyFish : Fish
-(void)washSmellyDish:(Dish*)dish;
@end

Вы можете просмотреть объявление NSObject и просмотреть методы по умолчанию, которые, следовательно, также наследует любой NSObject.

приведенные выше примеры: Cook, Dish и Fish наследуются от NSObject, поэтому вы можете запросить [object isKindOfClass:[NSObject class]], что немного избыточно, поскольку почти любой объект objc в любом случае наследуется от NSObject в какой-то момент. Но SmellyFish здесь наследуется от Fish, поэтому вы можете спросить, является ли isKindOfClassРыба, SmellyFish или NSObject, и он должен ответить «да».

Если вам нужно быть уверенным, что какой-то объект относится к какому-то конкретному классу, вместо этого используйте

[obj isMemberOfClass:[Fish class]]

так что, может быть, вы делаете что-то вроде ..

-(FishDish *)fishDish {
    return [FishDish new]; 
}
-(Dish *)cook:(id)food {
    if ([food isKindOfClass:[Fish class]]) {
        return [self fishDish]; //FishDish must be a subclass of Dish 
    } else {
        return [[Dish alloc] init];
    }
}

видишь (id)food? id — это тип данных, специфичный для Objective-C, который представляет указатель C на некоторые (любые) NSObject . Вы также можете написать id<SuperFood> food, выражая, что вы ожидаете какой-то указатель, который следует определенному протоколу в качестве ссылки на объект. Что похоже на (SuperFood*)food, где SuperFood* объявляет, что это должен быть указатель на один объект SuperFood.

возможно, также рекомендуется избегать приведения типов перед тем, как запрашивать соответствие конкретным типам данных, потому что можно сделать вызов метода isKindOfClass бесполезным, просто приведя. Вы хотите проверить, поэтому избегайте кастинга. Потому что, если вы слишком часто выполняете приведение и на самом деле не знаете тип данных, указанный в ссылке, вам лучше просто использовать id или NSObject*. Эта практика также удерживает вас от импорта дополнительного кода только для объявления определенного типа данных, когда вы даже не используете его методы в этой части файла реализации.

Другие вопросы по теме