Тяжело найти более красочно описание паттерна Адаптер, чем пример из жизни каждого, кто покупал технику из США. Розетка! Вот почему не сделать одинаковую розетку всюду? Но нет, в США розетка с квадратными дырками, в Европе с круглыми, а в некоторых странах вообще треугольные. Следовательно – потому вилки на зарядный устройствах, и других устройствах питания тоже различные.
Представьте, что Вы едете в коммандировку в США. У Вас есть, допустим, ноутбук купленный в Европе – следовательно вилка на проводе от блока питания имеет круглые окончания. Что делать? Покупать зарядку для американского типа розетки? А когда вы вернетесь домой – она будет лежать у Вас мертвым грузом?
Потому, вероятнее всего, Вы приобретете один из адаптеров, которые надеваются на вилку, и которая позволяет Вам использовать старую зарядку и заряжаться от совершенно другой розетки.
Так и с Адаптером – он конвертит интерфейс класса – на такой, который ожидается.
Сам паттерн состоит из трех частей: Цели (target), Адаптера (adapter), и адаптируемого (adaptee).
В нашей с вами проблеме:
- Target – ноутбук со старой зарядкой.
- Adapter – переходник.
- Adaptee – розетка с квадртаными дырками.
Имплементации паттерна Адаптер в Objective-C может быть 2 (вероятно даже больше, но я вижу две):
Итак, первая – это простенькая имплементация. Пускай у нас есть объект ASBird, который реализует протокол ASBirdProtocol:
@protocol ASBirdProtocol <NSObject> - (void)sing; - (void)fly; @end @interface ASBird : NSObject <ASBirdProtocol> @end
@implementation ASBird
#pragma mark - ASBirdProtocol
- (void)sing {
NSLog(@"Tew-tew-tew");
}
- (void)fly {
NSLog(@"OMG! I am flying!");
}
@end
И пускай у нас есть объект ASRaven, у которого есть свой интерфейс:
@interface ASRaven : NSObject - (void)flySearchAndDestroy; - (void)voice; @end
@implementation ASRaven
- (void)flySearchAndDestroy {
NSLog(@"I am flying and seak for killing!");
}
- (void)voice {
NSLog(@"Kaaaar-kaaaaar-kaaaaaaar!");
}
@end
Чтобы использовать ворону в методах которые ждут птицу:) стоит создать так называемый адаптер:
@class ASRaven; @interface ASRavenAdapter : NSObject <ASBirdProtocol> - (instancetype)initWithRaven:(ASRaven *)raven; @end
И сама реализация. Как видим мы завернули ворона в обертку протоколом ASBirdProtocol.
@interface ASRavenAdapter ()
@property (nonatomic, strong) ASRaven *raven;
@end
@implementation ASRavenAdapter
- (instancetype)initWithRaven:(ASRaven *)raven {
self = [super init];
if (self) {
_raven = raven;
}
return self;
}
#pragma mark - ASBirdProtocol
- (void)sing {
[self.raven voice];
}
- (void)fly {
[self.raven flySearchAndDestroy];
}
@end
Ну и конечно же тест:
- (void)makeTheBirdTest:(id<ASBirdProtocol>)bird {
[bird fly];
[bird sing];
}
И тестовый код:
ASBird *bird = [ASBird new];
ASRaven *raven = [ASRaven new];
ASRavenAdapter *ravenAdapter = [[ASRavenAdapter alloc] initWithRaven:raven];
[self makeTheBirdTest:bird];
[self makeTheBirdTest:ravenAdapter];
Результат можно легко увидеть в логе:
2017-08-22 22:08:43.307 PatternsObjC[2072:94182] OMG! I am flying! 2017-08-22 22:08:43.307 PatternsObjC[2072:94182] Tew-tew-tew 2017-08-22 22:08:43.307 PatternsObjC[2072:94182] I am flying and seak for killing! 2017-08-22 22:08:43.307 PatternsObjC[2072:94182] Kaaaar-kaaaaar-kaaaaaaar!
Теперь более сложная реализация, которая все еще зависит от протоколов, но уже использует делегацию. Вернемся к нашему несчастному ноутбуку и зарядке: Допустим у нас есть базовый класс ASCharger:
@interface ASCharger : NSObject - (void)charge; @end
@implementation ASCharger
- (void)charge {
NSLog(@"C'mon I am charging");
}
@end
И есть протокол для европейской зарядки:
@class ASCharger; @protocol ASEuropeanNotebookChargerDelegate <NSObject> - (void)chargetNotebookRoundHoles:(ASCharger *)charger; @end
Если сделать просто реализацию – то получится тоже самое, что и в прошлом примере:) Потому, давайте добавим делегат:
@interface ASEuropeanNotebookCharger : ASCharger <ASEuropeanNotebookChargerDelegate> @property (nonatomic, weak) id<ASEuropeanNotebookChargerDelegate> delegate; @end
@implementation ASEuropeanNotebookCharger
- (instancetype)init {
self = [super init];
if (self) {
_delegate = self;
}
return self;
}
#pragma mark - ASCharger
- (void)charge {
[self.delegate chargetNotebookRoundHoles:self];
[super charge];
}
#pragma mark - ASEuropeanNotebookChargerDelegate
- (void)chargetNotebookRoundHoles:(ASCharger *)charger {
NSLog(@"Charging with 220 and round holes!");
}
@end
Как видим, у нашего класса есть свойство которое реализует тип EuropeanNotebookChargerDelegate. Так как, наш класс этот протокол реализует, он может свойсту присвоить себя, потому когда происходит вызов:
[self.delegate chargetNotebookRoundHoles:self];
просто вызывается свой же метод. Вы увидите дальше, для чего это сделано.
Теперь, давайте глянем что ж за зверь такой – американская зарядка:
@interface ASUSANotebookCharger : ASCharger - (void)chargeNotebookRectHoles:(ASCharger *)charger; @end
@implementation ASUSANotebookCharger
- (void)chargeNotebookRectHoles:(ASCharger *)charger {
NSLog(@"Charge Notebook Rect Holes");
}
@end
Как видим, в американской зарядке совсем другой метод и мировозрение. Давайте, создадим адаптер для зарядки:
@class ASUSANotebookCharger; @interface ASUSANotebookEuropeanAdapter : ASCharger <ASEuropeanNotebookChargerDelegate> - (instancetype)initWithUSANotebookCharger:(ASUSANotebookCharger *)charger; @end
@interface ASUSANotebookEuropeanAdapter ()
@property (nonatomic, strong) ASUSANotebookCharger *usaCharger;
@end
@implementation ASUSANotebookEuropeanAdapter
- (instancetype)initWithUSANotebookCharger:(ASUSANotebookCharger *)charger {
self = [super init];
if (self) {
_usaCharger = charger;
}
return self;
}
#pragma mark - ASCharger
- (void)charge {
ASEuropeanNotebookCharger *euroCharge = [ASEuropeanNotebookCharger new];
euroCharge.delegate = self;
[euroCharge charge];
}
#pragma mark - ASEuropeanNotebookChargerDelegate
- (void)chargetNotebookRoundHoles:(ASCharger *)charger {
[self.usaCharger chargeNotebookRectHoles:charger];
}
@end
Как видим, наш адптер реализует интерфейс ASEuropeanNotebookChargerDelegate и его метод chargetNotebookRoundHoles. Потому, когда вызывается метод charge – на самом деле создается тип европейской зарядки, ей присвается наш адаптер как делегат, и вызывается ее метод charge. Так как делегатом присвоен наш адаптер, при вызове метода chargetNotebookRoundHoles, будет вызыван этот метод нашего адаптера, который в свою очередь вызывает метод зардяки США:)
Давайте посмотрим тест код и вывод лога:
- (void)makeTheNotebookCharge:(ASCharger *)charger {
[charger charge];
}
И тестовый код:
ASEuropeanNotebookCharger *euroCharger = [ASEuropeanNotebookCharger new];
[self makeTheNotebookCharge:euroCharger];
ASUSANotebookCharger *useCharger = [ASUSANotebookCharger new];
ASUSANotebookEuropeanAdapter *adapter = [[ASUSANotebookEuropeanAdapter alloc] initWithUSANotebookCharger:useCharger];
[self makeTheNotebookCharge:adapter];
Лог нам выведет:
2017-08-22 22:51:17.450 PatternsObjC[2506:178392] Charging with 220 and round holes! 2017-08-22 22:51:17.451 PatternsObjC[2506:178392] C'mon I am charging 2017-08-22 22:51:17.451 PatternsObjC[2506:178392] Charge Notebook Rect Holes 2017-08-22 22:51:17.451 PatternsObjC[2506:178392] C'mon I am charging
Apple реализует Adapter своеобразно (примерно как и требование Еврокомиссии перейти на MicroUSB). Они это делают с помощью протоколов. Вы, можете быть, знакомы с протоколами UITableViewDelegate, UIScrollViewDelegate, NSCoding, NSCopying. К примеру, протокол NSCopying предоставляет любому классу стандартный метод copy.
Немає коментарів:
Дописати коментар