'Developer/Pattern'에 해당하는 글 3건






iOS 아키텍처를 공부하다 VIPER란걸 접하게 되었고,

VIPER을 정리하는 포스팅이다.


Apple에서는 MVC 아키텍처를 사용을 권하고 그에대한 개발문서도 있다(링크)

하지만 Apple이 설명하는 MVC는 Model이 View에 접근해 화면에 데이터를 뿌려주는게 아니라,

Controller가 View와 Model의 중간역할을 하기 때문에 MVP 패턴에 가깝다고 생각된다.


하지만 이런 아키텍쳐는 Controller(iOS에선 ViewController)에서 하는 일이 너무나 많아진다.

그래서 코드가 길어지고 중복되는 코드가 많아져서 단점이 있다.


그걸 해결하는게 바로 VIPER 이다.

VIPER란 패턴을 처음 접했지만, 기존에 내가 코딩하는 패턴과 크게 다르지 않았다.

역시 패턴은 어떠한 패턴이 중요한게 아니고, 상황에 맞게 간단하고, 이해하기 쉽고, 재사용이 쉬우며, 반복된 코딩이 없는게 중요하다는걸 다시한번 깨닫게 되었다.


일단 VIPER가 무엇이냐면,

View, Interactor, Presenter, Entity, Router의 약자이다.

iOS 프로젝트를 보다 명확하고 모듈화 된 구조로 개발할 수 있는 아키텍쳐이다.





위의 그림은 VIPER의 아키텍쳐이다.

하나씩 설명하자면,

View : Presenter가 보여주는 걸 보여주며, 사용자의 액션을 Presneter로 보내는 작업을 한다.

Interactor : Business logic을 포함하며, API나 DB로 부터 Data를 받아서 Entity(모델)을 생성한다.

Presenter : View에서 유저의 액션을 받고, Interactor에 data를 요청하고 받아서 View 에 그려준다.

Entity : Interactor에 의해 만들어지는 Model 이다.

Router : Navigation logic 을 담당하고 있으며, screen에서 다른  screen으로 화면이 변경되는 부분을 처리한다.



위에서 말했듯,

패턴은 중요하지 않은것 같다.

이해가 쉽고, 간단하고, 재사용이 쉬우며, 반복된 코드가 없는 프로그래밍이 중요하다 생각한다.




'Developer > Pattern' 카테고리의 다른 글

iOS 아키텍처 VIPER  (1) 2018.06.22
[Object-c]DecoratorPattern - 데코레이터 패턴  (0) 2016.04.20
[Object-c]FactoryPattern - 팩토리 패턴  (0) 2016.04.19

WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  0 , 댓글  1개가 달렸습니다.
  1. saintmonday 2018.07.07 02:18
    문과생인 저한테는 너무 어려워보이네요
    프로그래머분들 정말 대단하신 것 같아요
secret





데코레이터 패턴


- 테코레이터 패턴은 객체의 추가적인 요건을 동적으로 추가할 수 있습니다.

- 데코레이터는 서브 클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있습니다.

- 한 객체를 여러 개의 데코레이터로 감쌀 수 있습니다.

- 기존 코드를 수정하지 않고도 행동을 확장할 수 있습니다.


요약하면 기존의 코드를 수정하지 않고 객체를 여러개의 데코레이터로 감사서 유연하게 확장해 추가적인 동작을 수행할 수 있습니다.



코드 설명


여행을 계획을 짜는 도중에, 여행비용을 계산하기 위한 어플을 만들려고 합니다.

자유여행을 위해 먹는것과 노는것은 직접 가서 결정하기로 하고, 여행 가기전에는 에어텔(비행기+호텔)만 예약하려고 합니다.

여러가지 조합이 있어서 선택 후 가격을 알아보려고 합니다.


투어 클래스에서 항공사, 호텔 마다 클래스를 만들수가 없어서 각각의 클래스를 데코레이터로 만들어서,

내가 결정한 항공사와, 호텔을 데코레이터로 감싸고,

최종적으로 내가 감싼 비행기와 호텔의 가격을 알기 위한 코드입니다.



코드




Tour.h

#import <Foundation/Foundation.h>


@interface Tour : NSObject {

    NSString *description;

}


-(NSString*)getDescription;

-(double)cost;


@end


Tour.m

#import <Foundation/Foundation.h>


@interface Tour : NSObject {

    NSString *description;

}


-(NSString*)getDescription;

-(double)cost;


@end


기본 Tour클래스로 설명을 볼수 있는 Description과 가격을 알수있는 cost 함수가 있다.




KoreanAir.h


#import "Tour.h"


@interface KoreanAir : Tour


@end



KoreanAir.m


#import "KoreanAir.h"


@implementation KoreanAir


- (instancetype)init {

    self = [super init];

    

    if (self) {

        description = @"Airplane - KoreanAir";

    }

    

    return self;

}


- (double)cost {

    return 100;

}


@end


Tour클래스를 상속받은 비행기클래스이다.

생성자에 비행기 정보와 비용을 설정한다.




AsianaAir.h


#import "Tour.h"


@interface AsianaAir : Tour


@end


AsianaAir.m

#import "AsianaAir.h"


@implementation AsianaAir


- (instancetype)init {

    self = [super init];

    

    if (self) {

        description = @"Airplane - AsianaAir";

    }

    

    return self;

}

- (double)cost {

    return 101;

}


@end


비행기 클래스로 위의 

Korean클래스와 같다.




JinAir.h


#import "Tour.h"


@interface JinAir : Tour


@end


JinAir.m


#import "JinAir.h"


@implementation JinAir



- (instancetype)init {

    self = [super init];

    

    if (self) {

        description = @"Airplane - JinAir";

    }

    

    return self;

}


- (double)cost {

    return 80;

}


@end


비행기 클래스로 위의 

Korean클래스와 같다.




HotelDecorator.h


#import "Tour.h"


@interface HotelDecorator : Tour


@end


HotelDecorator.m


#import "HotelDecorator.h"


@implementation HotelDecorator


- (NSString *)getDescription {

    NSAssert(NO, @"This is an abstract method so should be overridden.");

    return nil;

}


@end


HotelDecorator 클래스는 Tour클래스를 상속받아서 생성합니다. 모든 호텔클래스는 이 클래스를 기반으로 합니다.



HennanRagoonHotel.h


#import "HotelDecorator.h"

#import "Tour.h"

@interface HennanRagoonHotel : HotelDecorator{

    Tour *_tour;

}

- (instancetype)initWithTour:(Tour *)tour;

@end


HennanRagoonHotel.m


#import "HennanRagoonHotel.h"


@implementation HennanRagoonHotel


- (instancetype)initWithTour:(Tour *)tour{

    self = [super init];

    

    if (self) {

        _tour = tour;

    }

    

    return self;

}


- (NSString *)getDescription {

    return [NSString stringWithFormat:@"%@%@",_tour.getDescription,@", HeaanaRagoonHotel"];

}


- (double)cost {

    return 10 + _tour.cost;

}


@end


HotelDecorator를 상속받아 생성합니다. 헤난라군 클래스는 Tour클래스를 인스턴스 변수로 가지고 있고, 나중에 래핑할 때 이용됩니다.

헤난라군 클래스는 초기화 할때, Tour객체를 인자값을 넣고 getDescription과 cost를 구현합니다. 이 객체는 다른 객체를 중복해서 포함 해서 총 비용을 구할 수 있습니다.




ShangrilaHotel.h


#import "HotelDecorator.h"

#import "Tour.h"


@interface ShangrilaHotel : HotelDecorator {

    Tour *_tour;

}

- (instancetype)initWithTour:(Tour *)tour;


@end


ShangrilaHotel.m


#import "ShangrilaHotel.h"


@implementation ShangrilaHotel


- (instancetype)initWithTour:(Tour *)tour{

    self = [super init];

    

    if (self) {

        _tour = tour;

    }

    

    return self;

}


- (NSString *)getDescription {

    return [NSString stringWithFormat:@"%@%@",_tour.getDescription,@", ShangrilaHotel"];

}


- (double)cost {

    return 50 + _tour.cost;

}


@end


HennanRagoonHotel 클래스와 구조는 같습니다.



Thelindhotel.h

#import "HotelDecorator.h"

#import "Tour.h"


@interface ThelindHotel : HotelDecorator {

    Tour *_tour;

}

- (instancetype)initWithTour:(Tour *)tour;


@end


Thelindhotel.m


#import "ThelindHotel.h"


@implementation ThelindHotel



- (instancetype)initWithTour:(Tour *)tour{

    self = [super init];

    

    if (self) {

        _tour = tour;

    }

    

    return self;

}


- (NSString *)getDescription {

    return [NSString stringWithFormat:@"%@%@",_tour.getDescription,@", TheLindHotel"];

}


- (double)cost {

    return 30 + _tour.cost;

}



@end


HennanRagoonHotel 클래스와 구조는 같습니다.




main.m

#import "Tour.h"


#import "KoreanAir.h"

#import "AsianaAir.h"

#import "JinAir.h"


#import "HotelDecorator.h"


#import "HennanRagoonHotel.h"

#import "ShangrilaHotel.h"

#import "ThelindHotel.h"


int main(int argc, char * argv[]) {

    @autoreleasepool {

        //return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

        

        //진에어를 타고가서, 헤난라군2, 샹글릴라 1박했을 경우 입니다.

        Tour *tour1 = [JinAir new];

        tour1 = [[HennanRagoonHotel alloc] initWithTour:tour1];

        tour1 = [[HennanRagoonHotel alloc] initWithTour:tour1];

        tour1 = [[ShangrilaHotel alloc] initWithTour:tour1];

        

        NSLog(@"%@ $%0.2f",tour1.getDescription,tour1.cost);

        

        //진에어를 타고가서, 더린드 1, 샹그릴라 2 했을 경우 입니다.

        Tour *tour2 = [JinAir new];

        tour2 = [[ThelindHotel alloc] initWithTour:tour2];

        tour2 = [[ShangrilaHotel alloc] initWithTour:tour2];

        tour2 = [[ShangrilaHotel alloc] initWithTour:tour2];

        

        NSLog(@"%@ $%0.2f",tour2.getDescription,tour2.cost);

        

        //대한항공을 타고가서, 샹그릴라3 했을 경우입니다.

        Tour *tour3 = [KoreanAir new];

        tour3 = [[ShangrilaHotel alloc] initWithTour:tour3];

        tour3 = [[ShangrilaHotel alloc] initWithTour:tour3];

        tour3 = [[ShangrilaHotel alloc] initWithTour:tour3];

        

        NSLog(@"%@ $%0.2f",tour3.getDescription,tour3.cost);


    }

    return 0;

}


2016-04-20 12:03:35.358 04_DecoratorPattern[1675:98082] Airplane - JinAir, HeaanaRagoonHotel, HeaanaRagoonHotel, ShangrilaHotel $150.00

2016-04-20 12:03:35.359 04_DecoratorPattern[1675:98082] Airplane - JinAir, TheLindHotel, ShangrilaHotel, ShangrilaHotel $210.00

2016-04-20 12:03:35.359 04_DecoratorPattern[1675:98082] Airplane - KoreanAir, ShangrilaHotel, ShangrilaHotel, ShangrilaHotel $250.00


'Developer > Pattern' 카테고리의 다른 글

iOS 아키텍처 VIPER  (1) 2018.06.22
[Object-c]DecoratorPattern - 데코레이터 패턴  (0) 2016.04.20
[Object-c]FactoryPattern - 팩토리 패턴  (0) 2016.04.19

WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  0 , 댓글  0개가 달렸습니다.
secret





(소스 주소 : https://github.com/minjoongkim/iOS-Pattern)



팩토리 패턴이란?

객체 생성을 처리하는 패턴입니다. 


어떠한 경우에 사용하는가?

예를들어 책이라는 클래스가 있고, 

그 하위에 만화책, 소설책이라는 클래스가 있습니다.


만화책, 소설책은 책이라는 큰 범위안에 각자가 해야할 메소드들이 따로 있습니다.


어떠한 정보를 받아서 클래스로 생성할때, 매번 if문으로 만화책인지 소설책인지 판단해서 클래스를 만들게 되면 번거롭고 틀릴수도 있기 때문에 이 부분을 띄어서(나눠서) 팩토리 메소드를 만들게 됩니다.

그렇게 되면 객체를 생성할때마다 팩토리 메소드에 객체생성을 하도록 지시하면 객체를 생성해서 반환하게 됩니다.



팩토리 패턴을 사용하게 되면?

이렇게 되면, 객체생성의 버그를 줄일수 있고 유연성을 증가시킬수 있습니다.



우선 book 클래스와 하위 클래스를 먼저 살펴보겠습니다.


부모가 될 Book 클래스


Book.h

#import <Foundation/Foundation.h>


@interface Book : NSObject {

    NSString *name;

    int price;

    NSString *category;

}


-(void)setName:(NSString*)bookName;

-(NSString*)getName;

-(void)setPrice:(int)bookprice;

-(int)getPrice;

-(void)setCategory:(NSString*)bookCategory;

-(NSString*)getCategory;

-(void)bookInfo;


@end


Book.m

#import "Book.h"


@implementation Book


-(void)setName:(NSString*)bookName {

    name = bookName;

}

-(NSString*)getName {

    return name;

}

-(void)setPrice:(int)bookprice {

    price = bookprice;

}

-(int)getPrice {

    return price;

}


-(void)setCategory:(NSString*)bookCategory {

    category = bookCategory;

}

-(NSString*)getCategory {

    return category;

}


-(void)bookInfo {

    NSLog(@"%@ : %@ book price is %d", category, name, price);

}


@end



Book 클래스의 하위 클래스 ComicBook


ComicBook.h


#import <Foundation/Foundation.h>

#import "Book.h"

@interface ComicBook : Book


-(id)init:(NSString*)bookName price:(int)bookprice;

@end



ComicBook.m


#import "ComicBook.h"


@implementation ComicBook


-(id)init:(NSString*)bookName price:(int)bookprice {

    self = [super init];

    if (self) {

        [self setName:bookName];

        [self setPrice:bookprice];

        [self setCategory:@"Comic"];

    }

    return self;

}


@end



Book 클래스의 하위 클래스 NovelBook


NovelBook.h


#import <Foundation/Foundation.h>

#import "Book.h"

@interface NovelBook : Book

-(id)init:(NSString*)bookName price:(int)bookprice;

@end



NovelBook.m


#import "NovelBook.h"


@implementation NovelBook


-(id)init:(NSString*)bookName price:(int)bookprice {

    self = [super init];

    if (self) {

        [self setName:bookName];

        [self setPrice:bookprice];

        [self setCategory:@"Novel"];

    }

    return self;

}


@end



이제 이 클래스들을 이용해 객체를 생성해보겠습니다.


팩토리 패턴 없이 객체를 생성할 때,


Book *book;

NSString *bookCategory = @"Comic";

if([bookCategory isEqualToString:@"Comic"]) {

    book = [[ComicBook alloc]init:@"SlamDunk" price:5000];

}else if([bookCategory isEqualToString:@"Novel"]) {

    book = [[NovelBook alloc]init:@"HarryPotter" price:13000];

}


[book bookInfo];


만약 Book을 상속받는 클래스가 많다면 코드마다 판단해서 객체를 넣기엔 너무나 많은 조건문이 들어가고, 

객체 생성하는 부분이 한군데가 아니라면 그 모든곳을 다 고쳐줘야 하는 번거로움이 있고, 가독성이 떨어집니다.


그래서 이번엔 팩토리 클래스를 만들고 객체를 생성해보겠습니다.



팩토리 클래스 BookFactory


BookFactory.h


#import <Foundation/Foundation.h>

#import "Book.h"


@interface BookFactory : NSObject

+(Book*)makeBook:(NSString*)bookType name:(NSString*)bookName price:(int)bookPrice;

@end


BookFactory.m


#import "BookFactory.h"

#import "NovelBook.h"

#import "ComicBook.h"


@implementation BookFactory


+(Book*)makeBook:(NSString*)bookType name:(NSString*)bookName price:(int)bookPrice{

    if([bookType isEqualToString:@"Comic"]) {

        return [[ComicBook alloc] init:bookName price:bookPrice];

    }else if([bookType isEqualToString:@"Novel"]) {

        return [[NovelBook alloc] init:bookName price:bookPrice];

    }else {

        return nil;

    }

}


@end



팩토리 패턴으로 객체를 생성할 때,


Book *bookUseFactoryComic = [BookFactory makeBook:@"Comic" name:@"SlamDunk" price:5000];

[bookUseFactoryComic bookInfo];


Book *bookUseFactoryNovel = [BookFactory makeBook:@"Novel" name:@"HarryPotter2" price:13000];

[bookUseFactoryNovel bookInfo];


조건문으로 객체를 생성하던 부분이 클래스로 빠져나가서 객체생성을 캡슐화하고 유연성을 증가 시킬수 있습니다.



'Developer > Pattern' 카테고리의 다른 글

iOS 아키텍처 VIPER  (1) 2018.06.22
[Object-c]DecoratorPattern - 데코레이터 패턴  (0) 2016.04.20
[Object-c]FactoryPattern - 팩토리 패턴  (0) 2016.04.19

WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  0 , 댓글  0개가 달렸습니다.
secret