Тяжело найти более красочно описание паттерна Адаптер, чем пример из жизни каждого, кто покупал технику из США. Розетка! Вот почему не сделать одинаковую розетку всюду? Но нет, в США розетка с квадратными дырками, в Европе с круглыми, а в некоторых странах вообще треугольные. Следовательно – потому вилки на зарядный устройствах, и других устройствах питания тоже различные.
Представьте, что Вы едете в коммандировку в США. У Вас есть, допустим, ноутбук купленный в Европе – следовательно вилка на проводе от блока питания имеет круглые окончания. Что делать? Покупать зарядку для американского типа розетки? А когда вы вернетесь домой – она будет лежать у Вас мертвым грузом?
Потому, вероятнее всего, Вы приобретете один из адаптеров, которые надеваются на вилку, и которая позволяет Вам использовать старую зарядку и заряжаться от совершенно другой розетки.
Так и с Адаптером – он конвертит интерфейс класса – на такой, который ожидается.
Сам паттерн состоит из трех частей: Цели (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.
Немає коментарів:
Дописати коментар