2013-04-05 3 views
0

Скажем, у меня есть псевдо-абстрактный базовый класс, который пользователи не должны создавать. В основном я хочу бросить предупреждение, когда они пытаются вызвать init в классе, или вернуть один из конкретных экземпляров со значениями по умолчанию.Запретить вызов метода «абстрактного» класса ObjC из класса при разрешении [super init]?

Однако конкретные реализации этого базового класса должны вызвать [super init] в их инициализаторах. Конечно, это должно быть разрешено.

Как бы я лучше всего это сделал?

Я думал, что это должно быть прекрасно:

@implementation KTPhysicsShape 
-(id) init 
{ 
    // throw exception here or return concrete instance with default values 
} 

// this is what subclasses would call in place of [super init]: 
-(id) internal_initFromSubclass 
{ 
    return [super init]; 
} 
@end 

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

+0

выглядит довольно нормально. но что делать, если вы вызываете вызов 'internal_initFromSubclass' в' init' вместо исключения исключения. Таким образом, в классе будет только ваш метод, который мог бы инициализировать объект. – samfisher

ответ

3

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

Если вам требуется, чтобы ваши подклассы выполняли определенную инициализацию в -init, ваше решение может быть единственным. Но если вы просто хотите, чтобы убедиться, что они имеют подклассы, вы можете сделать это в -init:

-(id) init 
{ 
    NSAssert(![self isMemberOfClass:[KTPhysicsShape class]], 
             @"KTPhysicsShape must be subclassed!"); 
    return [super init]; 
} 
+0

Я думаю, вы хотели 'isMemberOfClass:' –

+0

Спасибо, Майк. На мой взгляд, это «тот метод, который не является« -isKindOfClass: '». –

+0

@SeamusCampbell не должен быть назначен? –

2

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

Суть недостатка в том, что у вас есть абстрактный класс, который не кажется действительно абстрактным; он может иметь конкретные экземпляры и требует конкретной инициализации.

Во-первых, почему вы не можете разбить класс на действительно абстрактный класс и конкретный класс?

Если вы не можете (или не хотите - конечно, больше классов имеют затраты на своем собственном), то одно решение, чтобы вырваться из часто используемых операций инициализации в отдельный метод:

- (void) commonKTPhysicsShapeInit 
{ 
    .... 
} 

Это не называется super. Это не будет объявлено в вашем заголовке; это метод «только внутренняя реализация», поэтому имя.

Затем позвольте вашим подклассам звонить через стандартный назначенный инициализатор, который вызывает commonInit. Для конкретных экземпляров этого класса есть отдельный инициализатор, который вызывает как commonInit, так и конкретный танец инициализации.

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

+0

полностью согласен с тем, что не нарушил цепочку init ... одна из причин, почему я спрашивал – LearnCocos2D