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

좋은 아키텍처란?

Developer 2018. 6. 22. 13:28






각각의 객체들은 구체적이고, 명확한 역할을 가지고 있어야 한다고 생각한다..

코드만 봐도 이게 무엇을 하는것인지 명확히 알 수 있어야 되고, 쉽게 이해할 수 있고, 쉽게 변경할 수 있어야 한다.


또한, 에러가 발생했을 때 쉽게 디버깅이 가능하도록, 단순한 데이터 흐름을 가져야 한다.

데이터는 단방향 흐름의 형태로 해야, 특정 지점에 브레이킹을 설정하고 데이터에 어떠한 변화가 일어나는지 쉽게 확인 할수 있어야 하지, 여러개의 다른 객체들 사이를 왔다 갔다 하거나, 같은 공유 자원을 조작해서 사용할 경우 오류의 원인을 찾기 힘들기 때문에 피하는 것이 좋다.


 아키텍처는 쉽게 이해할 수 있고 쉽게 바꿀수 있도록 단순해야 한다. 추상 클래스가 존재하고 시간이 갈 수록 개발자 본인도 이해하지 못하는 구조나 새로운 기능을 추가하는데 많은 고생을 해야하는 구조라면 올바른 아키텍처가 아니다.



'Developer' 카테고리의 다른 글

좋은 아키텍처란?  (0) 2018.06.22

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

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





애플에서  푸쉬서비스 토큰 방식이 추가되어서 알아봤다.


중요개선사항

1. HTTP/2 로 영구 푸시를 기반으로하는 새로운 프로토콜을 도입했다. 

   HTTP/2는 단일 연결을 통해 여러개의 스트림을 지원하는 매우 빠른 바이너리 프로토콜입니다.

2. 프로토콜은 더 이상 활성화되지 않은 장치 토큰을 나타내는 즉각적인 피드백 제공을 지원합니다. 푸쉬를 보내는 API를 호출했을 때, PUSH가 전달되지 않으면 Response로 알려주는게 추가되었다.

3. 페이로드가 최대 4KB로 증가되었다.


하나의 생성된 Key 토큰으로 개발자의 푸쉬를 보낼수가 있습니다.

이로 인해 많은 개발자가 여러 인증서를 관리, 갱신 및 취소 할 수있는 어려움이 줄어 들었습니다.



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

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





NSDate *pastDate = ...;

NSDate *nowDate = [NSDate date]; NSCalendar *calendar = [NSCalendar currentCalendar]; NSUInteger calendarUnit = NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;


NSDateComponents *dateComp = [calendar components: calendarUnit fromDate: nowDate toDate: pastDate options:0]; NSInteger day = dateComp.day; NSInteger hour = dateComp.hour; NSInteger min = dateComp.minute; NSInteger sec = dateComp.second;



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

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





iOS는 왜 해킹에 강할까? 라는 의문점을 가지고 찾아 보는 중에 DEP라는 매커니즘이 적용된 것을 알 수 있었다.


01 DEP란?

프로세서가 데이터 영역은 실행하지 않고 코드 영역만 구동하도록 메모리에서 코드 영역과 데이터 영역을 구분하기 위한 메커니즘.

즉, 공격 코드가 다운로드한 코드를 실행시키는 것을 막을 수 있다는 뜻이다.


다운로드한 코드는 DEP를 통해 데이터로 취급되어서 실행을 할 수 없게 된다.

그래서 iOS를 공격할 때는 ROP를 사용한단다. ( ROP에 대해선 나중에..)


02 iOS의 코드서명.

iOS의 코드서명은 매커니즘은 DEP와 비슷하지만 좀 더 강력하다.

DEP를 매커니즘이 되어있다면, ROP를 활용해 쓰고 실행할 수 있는 메모리 영역을 먼저 생성한 후, 공격에 필요한 페이로드를 작성하고 실행시키면 되지만,

iOS의 코드서명의 경우 신뢰할 수 있는 기관으로 부터 서명되지 않은 코드는 아무것도 실행시킬 수 없다.

따라서 ROP를 실행시킬 수 있을 뿐 페이로드는 실행하기 어렵다.




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

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





iOS10.0이 나온지 꽤 시간이 지났지만,

iOS 개발자로써 iOS가 새로 나올때마다 정리를 해둬야 될 것 같아서 뒤늦게 애플문서를 보고 정리를 해야겠다 생각하고 글을 쓴다.

자세한 내용

(https://developer.apple.com/library/prerelease/content/releasenotes/General/WhatsNewIniOS/Articles/iOS10.html#//apple_ref/doc/uid/TP40017084-SW1)


Providing Haptic Feedback

아이폰7, 아이폰7Plus 에서 기존 맥북에 있던 트랙패드처럼 홈버튼이 햅틱방식으로 변경되었다.

UIKit의 UIFeedbackGenerator로 사용할 수 있다.

아이폰7이 출시 된 후 만져본 느낌으론 어색했지만 적응이 된다면 상당히 괜찮을것 같다.


SiriKit

SiriKit이 오픈되었다. 일반 앱에서도 Siri를 이용한 앱을 만들수 있는데, 현재는 아래 종류의 앱만이 이용할 수 있다.

  • 음성/영상 전화
  • 메시지
  • 돈 주고받기
  • 사진찾기
  • 얘약관련
  • 운동 매니저
  • 카플레이
  • 레스토랑예약(애플제공한에서)
사용자가 서비스를 포함하는 요청을 하면 시리 킷은 사용자의 요청을 설명하고 요청에 해당되는 데이터를 제공할 수 있다.
자세한 사항은 SiriKit Programming Guide을 참고 해야 될 것 같다.

Proactive Suggestions

프로액티비는 검색을 통해 사용자에게 필요한 앱을 제안하는 기능이다. iOS9 에서는 App search를 적용했다면, Spotlight나 Safari 검색결과, Handoff, Siri 제안 등을 노출시킬 수 있었는데, iOS10에서는 사용자가 앱에서 어떤 동작을 하는지에 대한 정보를 제공해서 지도, 카플레이, 앱 전환, Siri대화, 잠금화면 등 앱을 추가적으로 노출시킬 수 있다.

Integrating with the Messages App

iOS10에서 크게 바뀐 것중 하나가 메시지 앱이다. 
코드를 건드릴 필요 없이 이미지만으로 스티커 앱을 만들 수있고, 커스텀 UI를 통해 텍스트,이미지,미디어 파일을 대화 안에서 생성하고 보내고 업데이트 할 수 있다. 단, Messages프레임워크를 사용해야 한다.

User Notifications

이번에 추가된 User Notifications 프레임워크는 기존에 로컬알림과 원격알림을 모두 관리할 수 있게 되었다. 플랫폼 간의 사용 용법이 통일화 되었고 playload를 디코드하여 이미지를 표시하거나 end-to-end  암호화를 지원할 수 있게 되었다.

Speech Recognition

연속적으로 음성 인식을 할 수있는 API가 추가되어, 녹음되거나 실시간으로 재생되는 음성을 인식하고 텍스트로 변환할 수 있게 되었다.
아래의 간단한 코드로 구현이 가능하다.

let recognizer = SFSpeechRecognizer()  
let request = SFSpeechURLRecognitionRequest(url: audioFileURL)  
recognizer?.recognitionTask(with: request, resultHandler: { (result, error) in  
     print (result?.bestTranscription.formattedString)
})
다만, 음성인식의 정확도를 높이기 위해 음성정보가 애플 서버에 임시적으로 저장되기 때문에 이를 위해 Info.plist에 사용자가 알 수 있도록 미리 표시해야 한다.

Wide Color

아이패드 프로에서 기존 sRGB보다 더 넓어진 확장된 sRGB(extended sRGB) 색영역을 표현할 수 있다. 이를 지원하기 위해 Core Graphics, Core Image, Metal, AVFoundation 프레임워크 들이 내부적으로 변경되었다.
  • iOS10에서, UIColor 클래스 확장 된 sRGB 색 공간을 사용하며, 그 초기화는 더 이상 0.0 내지 1.0의 원료 성분 값을 고정하지 않는다. (당신은 색상을 만들거나 그 구성 요소 값의 색상을 요구하는지 여부) 앱 구성 요소 값을 클램프은 UIKit에 의존하는 경우, 당신은 당신이 iOS10 에 연결하면 앱의 동작을 변경해야합니다.
  • 아이 패드 프로 (9.7 인치)에 UIView의 그리기 사용자 정의 수행 할 때, 기본 도면 환경을 확장 sRGB 색 공간으로 구성되어 있습니다.
  • 앱이 사용자 정의 이미지 오브젝트를 렌더링하는 경우, 대상 비트 맵이 확장 된 범위 또는 표준 범위 형식을 사용하여 생성되는지 여부를 제어하는 ​​새로운 UIGraphicsImageRenderer 클래스를 사용합니다.
  • 만약 그러한 코어 그래픽 또는 금속과 같은 낮은 레벨의 API를 사용하여 와이드 영역 기기에 자신의 화상 처리를 수행하는 경우에는 확장 된 범위의 컬러 공간 및 16 비트 부동 소수점 성분 값을 지원하는 픽셀 포맷을 사용한다. 색상 값의 클램핑하는 것이 필요한 경우, 당신은 이렇게 명시 적으로 수행해야합니다.
  • 코어 그래픽, 코어 이미지 및 금속 성능 쉐이더 쉽게 색 공간 사이의 색상과 이미지를 변환하기위한 새로운 옵션을 제공합니다.

Adapting to the True Tone Display

아이패드 프로 9.7인치에서 추가된 트루톤 디스플레이는 다양한 빛 환경에서 최적화된 색상으로 화면을 보여주게 된다. 이는 Info.plist 파일의 UIWhitePointAdaptivityStyle 키 값을 적절하게 설정할 수 있다.

App Search Enhancements

iOS10에서 CoreSpotlight 프레임워크를 통해 다음의 검색이 개선 되었다.
  • 앱 내의 검색
  • 이어서 검색 계속하기
  • 차동 개인 정보 보호와 딥 링크 인기를 크라우드 소싱
  • 유효성검증 결과의 시각화

Widget Enhancements

잠금화면에서도 위젯을 볼 수 있으며, 어떤 배경에서도 잘 보이게 개선되었다.

Apple Pay Enhancements

애플페이가 많이 확장되었다.

Security and Privacy Enhancements

iOS10은 몇 가지 변경 및 코드의 보안을 강화하고 사용자 데이터의 개인 정보를 유지하는 데 개선되었다.. 이러한 항목에 대한 자세한 내용은 https://developer.apple.com/security/를 참고할 수 있다

CallKit

CallKit 이 추가되어 VoIP앱이 일반 전화가 오는 것과 동일한 화면에 표시 할 수 있다. VoIP앱을 이요한 기록이 네이티브 전화 앱의 즐겨찾기와 최근 사용목록에도 표시되고 extension 을 통해 전화 관리나 전화를 건 사람에 대한 정보 표시가 가능해 우리나라 악질적인 스팸을 아이폰에서도 막을 수 있게 되었다.

News Publisher Enhancements

뉴스 게시자 쉽게 애플 뉴스 포맷을 사용 애플 뉴스 아름답게 디자인 뉴스, 잡지, 웹 콘텐츠를 제공 할 수있다. 누구나 독립 출판사와 블로거 주요 잡지 또는 뉴스 조직에서 가입 할 수 있습니다. 
자세한 사항은 visit https://newsresources.apple.com. 

Video Subscriber Account

iOS10에서 추가된 Video Subscriber Account를 통해 케이블 사업자나 위성 사업자에 구독중인 계정을 인증하고 이를 통해 앱에서 권한을 조절할 수 있게 되었다. Apple TV에서 각 채널별로 회원가입하고 로그인하고 복잡하게 진행된던 점이 SSO로 해결을 할 수 있을것으로 예상된다.

App Extensions
iOS 10에서 추가된 앱 익스텐션은 다음과 같다.
  • Call Directory

  • Intents

  • Intents UI

  • Messages

  • Notification Content

  • Notification Service

  • Sticker Pack



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

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





1. NSOperation

- 고수준의 편리한 API제공한다.(KVO 키 감시, operation cancel제어등..)

- 일반 스레드보다 약간 처리속도에 손실이 있다고 한다. 그럼에도 불구하고 편리한 메소드들은 이러한 단점을 커버하기 충분하다.

- GCD의 객체형이라고 볼 수 있을까?

사용후 알아서 메모리 해제.


2. GCD

- 블록으로 구현되어 있어 간단하게 사용가능하고 또한 코드 가독성도 좋다.

- 저수준 스레드 구현가능되기에 속도면에서 좋다고 한다. 다만, 상태감시, 오퍼레이션 캔슬 등등 다 직접 만들어야 한다는 귀찮음을 동반한다.

GCD는 NSOperation과 유사합니다. GCD는 C언어 기반이라는 차이점을 갖습니다.

사용후 직접 메모리 해제.



NSOperation과 GCD의 차이가 뭘지 궁금해서 검색해서 찾아본 결과인데,

잘못된 정보가 있다면 댓글 부탁드립니다.




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

트랙백  0 , 댓글  2개가 달렸습니다.
  1. 비밀댓글입니다
secret





- ViewController.m : 아이폰에서 값을 보낼 화면에서 다음과 같이 하시면 됩니다.


#import <WatchConnectivity/WatchConnectivity.h>

@interface ViewController () <WCSessionDelegate>


@end


- (void)viewDidLoad {

    [super viewDidLoad];

    //WCSession을 activate합니다.

    if(WCSession.isSupported){

        WCSession* session = WCSession.defaultSession;

        session.delegate = self;

        [session activateSession];

    }

}

-(void)packageAndSendMessage{

    //보낼 Dictionary를 설정합니다.

    NSDictionary* request = "보낼 Dictionary";

    

    if(WCSession.isSupported){

        WCSession* session = WCSession.defaultSession;

        session.delegate = self;

        [session activateSession];

        if(session.reachable)

        {

            //sendMessage로 Dictionary를 전송합니다.

            [session sendMessage:request replyHandler: ^(NSDictionary<NSString *,id> * __nonnull replyMessage)

             {

                 dispatch_async(dispatch_get_main_queue(), ^{

                     NSDictionary* message = replyMessage;

                     NSString* response = message[@"response"];

                     if(response) {

                         NSLog(@"response = %@", response);

                     }

                     else {

                         NSLog(@"nil");

                     }

                 });

             }

                errorHandler:^(NSError * __nonnull error) {

                    dispatch_async(dispatch_get_main_queue(), ^{

                        NSLog(@"error = %@",error.localizedDescription);

                    });

                }

             ];

        }

        else

        {

            NSLog(@"Session Not reachable");

        }

        

    }

    else

    {

        NSLog(@"Session Not Supported");

    }

}




- InterfaceController.m

#import <WatchConnectivity/WatchConnectivity.h>

@interface InterfaceController() <WCSessionDelegate>
- (void)session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *, id> *)message replyHandler:(void(^)(NSDictionary<NSString *, id> *replyMessage))replyHandler
{
    if(message){
        NSString* command = [message objectForKey:@"보낸 Dictionary로 값을 가져오면 됩니다."];
        NSLog(@"command = %@", command);
    }
}

/**
 Standard WatchKit delegate
 */
-(void)sessionWatchStateDidChange:(nonnull WCSession *)session
{
    if(WCSession.isSupported){
        WCSession* session = WCSession.defaultSession;
        session.delegate = self;
        [session activateSession];
        
    }
}



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

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





KeyChain 이란?

KeyChain은 iOS의 다양한 응용 프로그램에서 비밀번호나 계정등을 저장하는 암호화 되어있는 저장소입니다.

안전한 앱을 만들기 위해서 보안이 필요한 요소들을 분리하고 암호화 하는 노력이 필요합니다.

여기에서는 안전하게 데이터를 저장하기 위한 장소인 Keychain에 대해서 설명하고 어떻게 사용하는지 알아보겠습니다.


KeychainItemWrapper 클래스 사용하기

Keychain의 API들은 Security.framework에 정의되어 있는데 C함수로 정의되어 있습니다. 이들을 사용하기 위해 애플에서 제공해주는 KeychainItemWrapper를 사용하는 경우가 많습니다. (https://developer.apple.com/library/ios/samplecode/GenericKeychain/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007797-Intro-DontLinkElementID_2)

하지만 arc가 적용되어 있지 않기 때문에 arc가 적용된 버젼을 첨부파일에 첨부하겠습니다.


 KeychainItemWrapper.h



KeyChain을 사용하는 방법

알아볼 예제에서는 UDID를 불러와서 저장하는 방법을 해보겠습니다.


KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"UUID" accessGroup:nil];


NSString *uuid = [wrapper objectForKey:(__bridge id)(kSecAttrAccount)];


if( uuid == nil || uuid.length == 0)

{

    // if there is not UUID in keychain, make UUID and save it.

    uuid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];

    // save UUID in keychain

    [wrapper setObject:uuid forKey:(__bridge id)(kSecAttrAccount)];

}


NSLog(@"UUID = %@", uuid);


정리

앱에서 저장해야할 중요한 정보를 저장하는게 좋을것 같습니다. 간단한 정보는 UserDefaults에 저장하면 간단하겠지만, 아이디라던가 지워지면 안되는 정보들을 저장할 용도로 쓰는 것이 좋을 것 같습니다.


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

트랙백  0 , 댓글  0개가 달렸습니다.
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