Wzorce projektowe: Fabryka Abstrakcyjna (Abstract Factory) w Objective-C

W poprzednim poście pokazałem implementację wzorca Fabryka Abstrakcyjna (ang. Abstract Factory) w języku Swift. Dziś pora na odpowiednik w Objective-C.

Opis wzorcahttp://pl.wikipedia.org/wiki/Fabryka_abstrakcyjna_(wzorzec_projektowy)

Aby łatwiej móc porównać obie wersje implementacji (Swift i Objective-C) będę trzymał się poprzedniego przykładu elementów UI tworzonych specjalnie na konkretną porę roku.

Stwórzmy interfejs Abstrakcyjnej Fabryki:

//
// UIComponentFactory.h
// AbstractFactory
 
#import "Button.h"
#import "View.h"
#import "Label.h"
 
@protocol UIComponentFactory <NSObject>
 
- (id<Button>)createButton;
- (id<View>)createView;
- (id<Label>)createLabel;
 
@end

Tak jak poprzednio, fabryka ma za zadanie ma stworzyć 3 rodzaje elementów: ButtonView i Label. I znowu tak jak ostatnio ButtonView i Label to protokoły zdefiniowane na potrzeby tego przykładu. Poniżej ich definicje, tym razem w Objective-C:

// Button.h
// AbstractFactory
 
@protocol Button <NSObject>
 
- (NSString*)textColor;
- (NSString*)backgroundColor;
 
@end
// View.h
// AbstractFactory
 
@protocol View <NSObject>
 
- (NSString*)backgroundImage;
 
@end
// Label.h
// AbstractFactory
 
@protocol Label <NSObject>
 
- (NSString*)textColor;
 
@end

Poza składnią w powyższych listingach, to nic się nie zmieniło jeśli chodzi o poprzedni przykład w Swift.

I tym razem pokażę również implementację powyższych protokołów dla 2 zestawów obieków: Winter i Summer. Po resztę zapraszam do  kodu z repozytorium na GitHub’ie.

Zestaw Winter

//
// WinterView.h
// AbstractFactory
 
#import <Foundation/Foundation.h>
#import "View.h"
 
@interface WinterView : NSObject <View>
 
@end
// WinterView.m
// AbstractFactory
 
#import "WinterView.h"
 
@implementation WinterView
 
- (NSString*)backgroundImage {
 return @"snowstorm.jpg";
}
 
@end
// WinterButton.h
// AbstractFactory
 
#import <Foundation/Foundation.h>
#import "Button.h"
 
@interface WinterButton : NSObject <Button>
 
@end
// WinterButton.m
// AbstractFactory
 
#import "WinterButton.h"
 
@implementation WinterButton
 
- (NSString*)textColor {
 return @"blue";
}
 
- (NSString*)backgroundColor {
 return @"white";
}
 
@end
// WinterLabel.h
// AbstractFactory
 
#import <Foundation/Foundation.h>
#import "Label.h"
 
@interface WinterLabel : NSObject <Label>
 
@end
// WinterLabel.m
// AbstractFactory
 
#import "WinterLabel.h"
 
@implementation WinterLabel
 
- (NSString*)textColor {
 return @"darkBlue";
}
 
@end

Zestaw Summer:

// SummerView.h
// AbstractFactory
 
#import <Foundation/Foundation.h>
#import "View.h"
 
@interface SummerView : NSObject <View>
 
@end
// SummerView.m
// AbstractFactory
 
#import "SummerView.h"
 
@implementation SummerView
 
- (NSString*)backgroundImage {
 return @"greenTrees.jpg";
}
 
@end
// SummerButton.h
// AbstractFactory
 
#import <Foundation/Foundation.h>
#import "Button.h"
 
@interface SummerButton : NSObject <Button>
 
@end
// SummerButton.m
// AbstractFactory
 
#import "SummerButton.h"
 
@implementation SummerButton
 
- (NSString*)textColor {
 return @"black";
}
 
- (NSString*)backgroundColor {
 return @"yellow";
}
 
@end
// SummerLabel.h
// AbstractFactory
 
#import <Foundation/Foundation.h>
#import "Label.h"
 
@interface SummerLabel : NSObject <Label>
 
@end
// SummerLabel.m
// AbstractFactory
 
#import "SummerLabel.h"
 
@implementation SummerLabel
 
- (NSString*)textColor {
 return @"orange";
}
 
@end

I tak samo jak ostatnio, pokażę kod tylko 2 konkretnych fabryk. Po więcej szczegółów zapraszam do kodu projektu na GitHubie:

// WinterComponentFactory.h
// AbstractFactory
 
#import <Foundation/Foundation.h>
#import "UIComponentFactory.h"
 
@interface WinterComponentFactory : NSObject <UIComponentFactory>
 
@end
// WinterComponentFactory.m
// AbstractFactory
 
#import "WinterComponentFactory.h"
#import "WinterButton.h"
#import "WinterView.h"
#import "WinterLabel.h"
 
 
@implementation WinterComponentFactory
 
- (id<Button>)createButton {
 return [[WinterButton alloc] init];
}
 
- (id<View>)createView {
 return [[WinterView alloc] init];
}
 
- (id<Label>)createLabel {
 return [[WinterLabel alloc] init];
}
 
@end
// SummerComponentFactory.h
// AbstractFactory
 
#import <Foundation/Foundation.h>
#import "UIComponentFactory.h"
 
@interface SummerComponentFactory : NSObject <UIComponentFactory>
 
@end
// SummerComponentFactory.m
// AbstractFactory
 
#import "SummerComponentFactory.h"
#import "SummerButton.h"
#import "SummerView.h"
#import "SummerLabel.h"
 
@implementation SummerComponentFactory
 
- (id<Button>)createButton {
 return [[SummerButton alloc] init];
}
 
- (id<View>)createView {
 return [[SummerView alloc] init];
}
 
- (id<Label>)createLabel {
 return [[SummerLabel alloc] init];
}
 
@end

Mamy już jasną strukturę elementów widoku, mamy zdefiniowane protokół fabryki i 2 fabryki konkretne. Pora na jakieś testy.

//
// AbstractFactoryTests.m
// AbstractFactoryTests
//
// Created by Marcin Krzych on 22/02/15.
// Copyright (c) 2015 Marcin Krzych. All rights reserved.
//
 
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
 
#import "WinterComponentFactory.h"
#import "AutumnComponentFactory.h"
#import "SpringComponentFactory.h"
#import "SummerComponentFactory.h"
 
@interface AbstractFactoryTests : XCTestCase
 
@end
 
@implementation AbstractFactoryTests
 
- (void)setUp {
 [super setUp];
 // Put setup code here. This method is called before the invocation of each test method in the class.
}
 
- (void)tearDown {
 // Put teardown code here. This method is called after the invocation of each test method in the class.
 [super tearDown];
}
 
   ...
 
- (void)testWinterFactory {
 id<UIComponentFactory> componentFactory = [[WinterComponentFactory alloc] init];
 id<Button> button = [componentFactory createButton];
 id<Label> label = [componentFactory createLabel];
 id<View> view = [componentFactory createView];
 
 XCTAssertEqualObjects([button backgroundColor], @"white", @"Incorrect button background color");
 XCTAssertEqualObjects([button textColor], @"blue", @"Incorrect button text color");
 
 XCTAssertEqualObjects([label textColor], @"darkBlue", @"Incorrect label text color");
 
 XCTAssertEqualObjects([view backgroundImage], @"snowstorm.jpg", @"Incorrect view background image");
}
 
- (void)testSummerFactory {
 id<UIComponentFactory> componentFactory = [[SummerComponentFactory alloc] init];
 id<Button> button = [componentFactory createButton];
 id<Label> label = [componentFactory createLabel];
 id<View> view = [componentFactory createView];
 
 XCTAssertEqualObjects([button backgroundColor], @"yellow", @"Incorrect button background color");
 XCTAssertEqualObjects([button textColor], @"black", @"Incorrect button text color");
 
 XCTAssertEqualObjects([label textColor], @"orange", @"Incorrect label text color");
 
 XCTAssertEqualObjects([view backgroundImage], @"greenTrees.jpg", @"Incorrect view background image");
}
 
@end

Dzięki wykorzystaniu fabryk i odpowiednio przemyślanej sieci obiektów, możemy z łatwością odseparować od siebie pewne procesy. Podmiana fabryki z jednej na drugą jest jak najbardziej w porządku z punktu widzenia funkcjonalności programu. Posługując się protokołami i wskaźnikami ogólnymi id na przykład tak

id<Button>

odcinamy się od konkretnej klasy, a interesuje nas jedynie funkcjonalność (protokół) danego obiektu, czyli jakie wiadomości może otrzymywać. Zachęcam do poczytania o tym wzorcu na Wiki i do stosowania go w projektach tam gdzie trzeba.


Projekt Xcode i pliki źródłowe z przykładowym kodem można znaleźć tutaj.


Ten post jest częścią cyklu o wzorcach projektowych w Objective-C i Swift. Strona Wzorce projektowe w Objective-C i Swift – Kompendium  zbiera w jednym miejscu wzorce i przypisane do nich artykuły. Zapraszam do lektury.

Dodaj komentarz