🙌
(?)
1. 🤓
• 

• https://swiftkorea.github.io/meetup/2#session-time-5

2018 

• 

• https://www.slideshare.net/godrm/letswift18-1
? 191
1%
58%
41%
( ) ( ?)
Software maintenance is
not 'keep it working like before'.
It is 'keep it being useful in a changing world'
- Jessica Kerr
• 

• 

•UI 

• 

• 

• 

• 

• 

• 

• , 

• 

•
https://soojin.ro/review/
https://google.github.io/eng-practices/review/
rpc: remove size limit on RPC server message freelist.
Servers like FizzBuzz have very large messages and would benefit
from reuse. Make the freelist larger, and add a goroutine that
frees the freelist entries slowly over time, so that idle servers
eventually release all freelist entries.
Construct a Task with a TimeKeeper to use its TimeStr and Now methods.
Add a Now method to Task, so the borglet() getter method can be removed
(which was only used by OOMCandidate to call borglet’s Now method). This
replaces the methods on Borglet that delegate to a TimeKeeper.
Allowing Tasks to supply Now is a step toward eliminating the dependency
on Borglet. Eventually, collaborators that depend on getting Now from
the Task should be changed to use a TimeKeeper directly, but this has
been an accommodation to refactoring in small steps.
Continuing the long-range goal of refactoring the Borglet Hierarchy.
, ,
?
?
?
?
?
?
+
+
Main - , , ,
Reusability - DRY, components, generics
Reliability - Exception handling and cleanup
Extensibility
Security - , , ,
Performance - , Lazy Loading, ,
Scalability -
Usability -
1. ( )
- 

- TDD 

- ( ) 

2. ( )
- , , 

- private public 

3. ( )
- 

- , 

4. ( )
- , 

- .
1.
, , ,
• 

• 

• 

• 

• 

• 

•
string, array
,
( )
2.
, ,
• 

• 

• 

• 

• 

• 

• 

•
3.
• 

• 

• , , , , 

• 

• , , , 

• , , 

• , ,
4.
SOLID .
1. SRP (Single-Responsibility Principle)
( , , ) .
.
2. OCP (Open-Close Principle)
, .
.
3. LSP (Liskov Substitution Principle)
( ) . ( )
.
4. DIP (Dependency-Inversion Principle)
. ( )
, .
5. ISP (Interface-Segregation Principle)
( ) .
Employee
+ calculatePay
+ reportHours
+ save
CFO
COO
CTO
#1
PayCalculator
EmployeeSaver
HourReporter
+ calculatePay
+ reportHours
+ saveEmployee
Employee
Data
#2
PayCalculator
EmployeeSaver
HourReporter
+ calculatePay
+ reportHours
+ saveEmployee
Employee
Data
Employee
Facade
+ calculatePay
+ reportHours
+ save
OutputView MyPoint
/
OutputView
MyPoint
<<protocol>>
Drawable
Protocol Oriented Programming
ShapeFactory
Point
type
ShapeType
<Protocol>
Line Triangle Rect Polygon
User1 User2 User3
Cafe
+foo +bar +hop
User1 User2
<<interface>>
FooCafe
+foo
User3
Cafe
+foo +bar +hop
<<interface>>
BarCafe
+bar
<<interface>>
HopCafe
+hop
GameManager
Player
Impl
<<interface>>
PlayerFactory
+makePlayer
Player
Factory
+makePlayer
<<interface>>
Player
( )
( )
5.
.
(Immutable)
(Mutable)
(Transaction Memory)
5. GRASP
9
GRASP 9
9가지 General Responsibility Assignment Software Patterns 집합

객체 책임을 할당하는 것은 OOD(객체 지향 설계) 핵심 설계 방법 중에 하나

개발자와 설계자라면 이런 패턴에 익숙해져야 함

1.정보 담당자 Information Expert

2.소유권한 Creator

3.컨트롤러 Controller

4.낮은 연결 Low Coupling

5.높은 응집도 High Cohesion

6.간접 참조 Indirection

7.다형성 Polymorphism

8.순수 조립 Pure Fabrication

9.변화 보호 Protected Variations
https://medium.com/@codesquad_yoda/ -grasp- -d5e37a1bb5dc
🧑💻
godrm / LadderGame OH-MY / LadderGame
master master
1. fork
2. clone
( )
step1
3. checkout -b step1
//
//
4. add / commit
5. push
step16. Pull Request
7.
master
http://github.com/godrm/LadderGame
class ReservationAgency {
func reserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation {
let movie = screening.getMovie()
var discountable = false
for condition in movie.getDiscountConditions() {
if condition.getType() == .Period {
discountable = screening.getWhenScreened().getDayOfWeek().equals(codition.getDayOfWeek()) &&
condition.getStartTime().compareTo(screening.getWhenScreend().toLocalTime()) <= 0 &&
condition.getEndTime().compareTo(screening.getWhenScreend().toLocalTime()) <= 0 &&
}
else {
discountable = condition.getSequence() == screening.getSequence()
}
if discountable { break }
}
var fee : Money
if discountable {
var discountAmount = Money.ZERO
switch movie.getMovieType() {
case AMOUNT_DISCOUNT:
discountAmount = movie.getDiscountAmount()
case PERCENT_DISCOUNT:
discountAmount = movie.getFee().times(movie.getDiscountPercent())
case NONE_DISCOUNT:
break
}
fee = movie.getFee().minus(discountAmount).times(audienceCount)
}
else {
free = movie.getFee()
}
return Reservation(custom, screening, fee, audienceCount)
}
}
ReservationAgency
ReservationAgency
class ReservationAgency {
func reserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation {
let discountable = mayDiscountable(screening)
let fee = calculateFee(with: screening, discountable: discountable, audienceCount: audienceCount)
return Reservation(screening, customer, audienceCount, fee)
}
}
2. 👨🎨
Abstraction
➔
main()
input() -> process() -> output()
validate() format()save()
➔
-
-
-
-
-
Client Employee
calculatePay()
monthlyBasePay()
SalariedEmployee HourlyEmployee
calculatePay()
monthlyBasePay()
calculatePay()
monthlyBasePay()
func calculatePay(taxRate: Double) -> Double {
if (hourly) {
return calculateHourlyPay(taxRate)
}
return calculateSalariedPay(taxRage)
}
• : 

	 goto 

• : 

	 + ( )

• : 

	 /
property method

encapsulation

inheritance

polymorphism

class instance

design pattern
Encapsulation
+
Main
HL1 HL2 HL3
ML1 ML2 ML3 ML4 ML5 ML6
LL1 LL1 LL3 LL4 LL5 LL6 LL7 LL8 LL9 LL10 LL11 LL12
HL1
ML1
+ Foo()
<<Protocol>>
AM
+ Foo()
호출을 하는 모듈과 호출 받는 모듈 모두 소스 코드 의존성을 원하는 방향으로 설정할 수 있다.

소스 코드 의존성을 원하는 방향으로 관리할 수 있기 때문에 

모듈, 컴포넌트 또는 배포가능한 단위로 서로 의존하지 않도록 컴파일할 수 있다. 

결국 서로 독립적으로 배포를 할 수 있다. (배포 독립성)
Financial
Report
Controller
Financial
Report
Presenter
<I>
Screen
Presenter
Screen
View
Model
Screen
View
Web
View
<DS>
<I>
Financial
Report
Request
Financial
Report
Requester
Financial
Report
Response
<DS>
<DS>
<I>
Financial
Report
Generator
Financial
Report
Gateway
<I>
Financial
Report
Entities
Financial
Data
Mapper
Financial
Database
Printer
Presenter
Print
View
Model
Print
View
PDF
View
<DS>
<I>
Screen Presenter Printer Presenter
Controller Interactor
Database
PDF ViewWeb View
Financial
Report
Controller
Financial
Report
Interactor
Financial
Database
PDF ViewWeb View
Screen
Presenter
Print
Presenter
http://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
<I>
<I>
View
View Model
Presenter
Controller
Input
Data
Input
Boundary
Output
Boundary
Output
Data
Data Access
Interface
Data Access Database
Entities
Use Case
Interactor
<I>
<DS>
<I>
<DS>
<I>
<DS>
// /
View Controller Model Presenter View
Model - View - Controller
Controller : Coordination
Model : Data View : Display
/
UIViewController
NSObject UIView
1. : , ?

2. : - ?

3. : ?

4. : , ?

5. : ?
Point of View
MVC
: objc App Architecture
MVVM + C
: objc App Architecture
VIPER
: objc App Architecture
3. 🧹
POP = Value Type + OOP + FP
Classes Are Awesome
• Encapsulation
• Access Control
• Abstraction
• Namespace
• Expressive Syntax
• Extensibility
ClassesClassesClassesTypes
I can do all
that with structs
and enums.
WWDC 2015 : POP
POP = Value Type + OOP
+ FP
class Ordered {
func precedes(other: Ordered) -> Bool { fatalError("implement me!") }
}
class Number : Ordered {
var value: Double = 0
override func precedes(other: Ordered) -> Bool {
return self.value < (other as! Number).value
}
}
OOP with Class
protocol Ordered {
func precedes(other: Self) -> Bool
}
struct Number : Ordered {
var value: Double = 0
func precedes(other: Number) -> Bool {
return self.value < other.value
}
}
POP with struct
• 



• 



.

• GRASP
:
1*
*
1
1 1..*
Information Expert
1:
? ?:Screening
Information Export
1:
?:Screening
2:
:Movie
3*:
:Discount
Condition
:Discount
Condition
:Discount
Condition
Screening Movie
Discount
Condition
1:
2:
3:
(High Cohesion) (Low Cohesion)
(High Coupling) (Low Coupling)
AA
Creator
1:
?:Screening
2:
:Movie
3*:
:Discount
Condition
:Discount
Condition
:Discount
Condition
:Reservation
4: <<create>>
class Screening {
func reserve(with customer: Customer, audienceCount: Int) -> Reservation {
}
}
class Screening {
private var movie : Movie
private var sequence : Int
private var whenScreened : DateTime
func reserve(with customer: Customer, audienceCount: Int) -> Reservation {
}
}
class Screening {
private var movie : Movie
private var sequence : Int
private var whenScreened : DateTime
func reserve(with customer: Customer, audienceCount: Int) -> Reservation {
return Reservation(with: customer, screening: self, fee:calculateFee(audienceCount), audienceCount)
}
func calculateFee(int audienceCount) -> Money {
return movie.calculateMovieFee(self).times(audienceCount)
}
}
Screening
class Movie {
func calculateMovieFee(screening: Screening) -> Money {
}
}
class Movie {
private let title : String
private let runningTime : TimeInterval
private let fee : Money
private var discountConditions = Array<DiscountCondition>()
private let movieType : MovieType
private let discountAmount : Money
private let discountPercent : Double
func calculateMovieFee(screening: Screening) -> Money {
}
}
Movie
class Movie {
enum MovieType {
case AmountDiscount
case PercentDiscount
case NoneDiscount
}
private let title : String
private let runningTime : TimeInterval
private let fee : Money
private var discountConditions = Array<DiscountCondition>()
private let movieType : MovieType
private let discountAmount : Money
private let discountPercent : Double
func calculateMovieFee(screening: Screening) -> Money {
if isDiscountable(for: screening) {
return fee.minus(calculateDiscountAmount())
}
return fee
}
private func isDiscountable(for: Screening) -> Bool {
return discountConditions.filter{ $0.isSatisfied(by: screening) }.count > 0
}
}
Movie
class DiscountCondition {
func isSatisfied(by screening: Screening) -> Bool {
}
}
class DiscountCondition {
private let type : DiscountConditionType
private let sequence : Int
private let dayOfWeek : DayOfWeek
private let startTime : Date
private let endTime : Date
func isSatisfied(by screening: Screening) -> Bool {
if type == .period {
return isSatisfiedByPeriod(screening)
}
return isSatisfiedBySequence(screening)
}
private func isSatisfiedByPeriod(_ screening: Screening) -> Bool {
return dayOfWeek.equals(screening.whenScreened.dayOfWeek) &&
startTime.compare(to: screening.whenScreended.toLocalTime() <= 0) &&
endTime.isAfter(to: screening.whenScreended.toLocalTime() >= 0)
}
private func isSatisfiedBySequence(_ screening: Screening) -> Bool {
return sequence == screening.sequence
}
}
DiscountCondition
class Screening {
private var movie : Movie
private(set) var sequence : Int
private(set) var whenScreened : DateTime
func reserve(with customer: Customer, audienceCount: Int) -> Reservation {
return Reservation(with: customer, screening: self, fee:calculateFee(audienceCount), audienceCount)
}
func calculateFee(int audienceCount) -> Money {
return movie.calculateMovieFee(self).times(audienceCount)
}
}
Screening & DiscountCondition
class DiscountCondition {
enum DiscountConditionType {
case Sequence, Period
}
private let type : DiscountConditionType
private let sequence : Int
private let dayOfWeek : DayOfWeek
private let startTime : DateTime
private let endTime : DateTime
//…
SRP ( )
class PeriodCondition {
private let dayOfWeek : DayOfWeek
private let startTime : Date
private let endTime : Date
init(dayOfWeek: DayOfWeek, startTime: DateTime, endTime: DateTime) {
self.dayOfWeek = dayOfWeek
self.startTime = startTime
self.endTime = endTime
}
func isSatisfied(by screening: Screening) -> Bool {
return dayOfWeek.equals(screening.whenScreened.dayOfWeek) &&
startTime.compare(to: screening.whenScreended.toLocalTime() <= 0) &&
endTime.isAfter(to: screening.whenScreended.toLocalTime() >= 0)
}
}
PeriodCondition - SequenceCondition
class SequenceCondition {
private let sequence : Int
init(with sequence: Int) {
self.sequence = sequence
}
func isSatisfied(by screening: Screening) -> Bool {
return sequence == screening.sequence
}
}
:Movie
:Period
Condition
:Movie
:Sequence
Condition
Moive
class Movie {
enum MovieType {
case AmountDiscount
case PercentDiscount
case NoneDiscount
}
private let title : String
private let runningTime : TimeInterval
private let fee : Money
private let movieType : MovieType
private let discountAmount : Money
private let discountPercent : Double
private var periodConditions = Array<PeriodCondition>()
private var sequenceConditions = Array<SequenceCondition>()
func calculateMovieFee(screening: Screening) -> Money {
if isDiscountable(for: screening) {
return fee.minus(calculateDiscountAmount())
}
return fee
}
private func mayPeriodConditions(with screening: Screening) -> Bool {
return periodConditions.filter{ $0.isSatisfied(by: screening) }.count > 0
}
private func maySequenceConditions(with screening: Screening) -> Bool {
return sequenceConditions.filter{ $0.isSatisfied(by: screening) }.count > 0
}
private func isDiscountable(for: Screening) -> Bool {
return mayPeriodConditions(with: screening) || maySequenceConditions(with: screening)
}
Movie
:Sequence
Condition
:Period
Condition
:Movie
:Discount
Condition
DiscountCondition
protocol DiscountCondition {
func isSatisfied(by screening: Screening) -> Bool
}
class PeriodCondition : DiscountCondition {
}
class SequenceCondition : DiscountCondition {
}
class Movie {
enum MovieType {
case AmountDiscount
case PercentDiscount
case NoneDiscount
}
private let title : String
private let runningTime : TimeInterval
private let fee : Money
private let movieType : MovieType
private let discountAmount : Money
private let discountPercent : Double
private var discountConditions = Array<DiscountCondition>()
func calculateMovieFee(screening: Screening) -> Money {
if isDiscountable(for: screening) {
return fee.minus(calculateDiscountAmount())
}
return fee
}
private func isDiscountable(for: Screening) -> Bool {
return discountConditions.filter{ $0.isSatisfied(by: screening) }.count > 0
}
Movie
Protected Variations
Movie
-title
+calculateMovieFee()
Screening <<interface>>
DiscountCondition
+ isSatisfiedBy()
SequenceCondition
+ isSatisfiedBy()
PeriodCondition
+ isSatisfiedBy()
+ reserve()
movie discountConditions
Movie
Movie
-title
+calculateMovieFee()
Screening <<interface>>
DiscountCondition
+ isSatisfiedBy()
SequenceCondition
+ isSatisfiedBy()
PeriodCondition
+ isSatisfiedBy()
+ reserve()
movie discountConditions
Percent
DiscountMovie
None
DiscountMovie
Amount
DiscountMovie
#calculateDiscountAmount() #calculateDiscountAmount() #calculateDiscountAmount()
anObject foobar[ ];
instance method
self anObject
action
message[ ]
anObject.foobar()
, ,
self anObject
1.
message[ ]
operation
method2.
3.
➔
before
class ReservationAgency {
func reserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation {
let movie = screening.getMovie()
var discountable = false
for condition in movie.getDiscountConditions() {
if condition.getType() == .Period {
discountable = screening.whenScreened().getDayOfWeek().equals(codition.getDayOfWeek()) &&
condition.getStartTime().compareTo(screening.whenScreend().toLocalTime()) <= 0 &&
condition.getEndTime().compareTo(screening.whenScreend().toLocalTime()) <= 0 &&
}
else {
discountable = condition.getSequence() == screening.getSequence()
}
if discountable { break }
}
var fee : Money
if discountable {
var discountAmount = Money.ZERO
switch movie.getMovieType() {
case AMOUNT_DISCOUNT:
discountAmount = movie.getDiscountAmount()
case PERCENT_DISCOUNT:
discountAmount = movie.getFee().times(movie.getDiscountPercent())
case NONE_DISCOUNT:
break
}
fee = movie.getFee().minus(discountAmount).times(audienceCount)
after
class ReservationAgency {
func reserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation {
let fee = screening.calculateFee(audienceCount)
return Reservation(custom, screening, fee, audienceCount)
}
}
exception
class PeriodCondition : DiscountCondition {
private let dayOfWeek : DayOfWeek
private let startTime : Date
private let endTime : Date
init(dayOfWeek: DayOfWeek, startTime: DateTime, endTime: DateTime) {
self.dayOfWeek = dayOfWeek
self.startTime = startTime
self.endTime = endTime
}
func isSatisfied(by screening: Screening) -> Bool {
return dayOfWeek.equals(screening.whenScreened.dayOfWeek) &&
startTime.compare(to: screening.whenScreended.toLocalTime() <= 0) &&
endTime.isAfter(to: screening.whenScreended.toLocalTime() >= 0)
}
}
class PeriodCondition : DiscountCondition {
private let dayOfWeek : DayOfWeek
private let startTime : Date
private let endTime : Date
//
func isSatisfied(by screening: Screening) -> Bool {
return screening.isDiscountable(dayOfWeek: dayOfWeek, startTime: startTime, endTime: endTime)
}
}
extension Screening {
func isDiscountable(dayOfWeek: DayOfWeek, startTime: DateTime, endTime: DateTime) -> Bool {
return dayOfWeek.equals(whenScreened.dayOfWeek) &&
startTime.compare(to: whenScreended.toLocalTime() <= 0) &&
endTime.isAfter(to: whenScreended.toLocalTime() >= 0)
}
}
–JK
“iOS ,
.”
Letswift19-clean-architecture

Letswift19-clean-architecture

  • 2.
  • 3.
  • 4.
  • 5.
    • • https://swiftkorea.github.io/meetup/2#session-time-5 2018 • • https://www.slideshare.net/godrm/letswift18-1
  • 6.
  • 7.
    Software maintenance is not'keep it working like before'. It is 'keep it being useful in a changing world' - Jessica Kerr
  • 9.
    • • •UI • • • • • • • , • • https://soojin.ro/review/ https://google.github.io/eng-practices/review/
  • 10.
    rpc: remove sizelimit on RPC server message freelist. Servers like FizzBuzz have very large messages and would benefit from reuse. Make the freelist larger, and add a goroutine that frees the freelist entries slowly over time, so that idle servers eventually release all freelist entries. Construct a Task with a TimeKeeper to use its TimeStr and Now methods. Add a Now method to Task, so the borglet() getter method can be removed (which was only used by OOMCandidate to call borglet’s Now method). This replaces the methods on Borglet that delegate to a TimeKeeper. Allowing Tasks to supply Now is a step toward eliminating the dependency on Borglet. Eventually, collaborators that depend on getting Now from the Task should be changed to use a TimeKeeper directly, but this has been an accommodation to refactoring in small steps. Continuing the long-range goal of refactoring the Borglet Hierarchy.
  • 11.
  • 12.
  • 13.
  • 14.
    Main - ,, , Reusability - DRY, components, generics Reliability - Exception handling and cleanup Extensibility Security - , , , Performance - , Lazy Loading, , Scalability - Usability -
  • 17.
    1. ( ) - - TDD - ( ) 2. ( ) - , , - private public 3. ( ) - - , 4. ( ) - , - .
  • 18.
    1. , , , • • • • • • •
  • 19.
  • 20.
  • 21.
    2. , , • • • • • • • •
  • 24.
    3. • • •, , , , • • , , , • , , • , ,
  • 26.
  • 27.
    1. SRP (Single-ResponsibilityPrinciple) ( , , ) . . 2. OCP (Open-Close Principle) , . . 3. LSP (Liskov Substitution Principle) ( ) . ( ) . 4. DIP (Dependency-Inversion Principle) . ( ) , . 5. ISP (Interface-Segregation Principle) ( ) .
  • 28.
  • 29.
  • 30.
    #2 PayCalculator EmployeeSaver HourReporter + calculatePay + reportHours +saveEmployee Employee Data Employee Facade + calculatePay + reportHours + save
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
    User1 User2 <<interface>> FooCafe +foo User3 Cafe +foo +bar+hop <<interface>> BarCafe +bar <<interface>> HopCafe +hop
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
    GRASP 9 9가지 GeneralResponsibility Assignment Software Patterns 집합 객체 책임을 할당하는 것은 OOD(객체 지향 설계) 핵심 설계 방법 중에 하나 개발자와 설계자라면 이런 패턴에 익숙해져야 함 1.정보 담당자 Information Expert 2.소유권한 Creator 3.컨트롤러 Controller 4.낮은 연결 Low Coupling 5.높은 응집도 High Cohesion 6.간접 참조 Indirection 7.다형성 Polymorphism 8.순수 조립 Pure Fabrication 9.변화 보호 Protected Variations https://medium.com/@codesquad_yoda/ -grasp- -d5e37a1bb5dc
  • 41.
  • 42.
    godrm / LadderGameOH-MY / LadderGame master master 1. fork 2. clone ( ) step1 3. checkout -b step1 // // 4. add / commit 5. push step16. Pull Request 7. master http://github.com/godrm/LadderGame
  • 44.
    class ReservationAgency { funcreserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation { let movie = screening.getMovie() var discountable = false for condition in movie.getDiscountConditions() { if condition.getType() == .Period { discountable = screening.getWhenScreened().getDayOfWeek().equals(codition.getDayOfWeek()) && condition.getStartTime().compareTo(screening.getWhenScreend().toLocalTime()) <= 0 && condition.getEndTime().compareTo(screening.getWhenScreend().toLocalTime()) <= 0 && } else { discountable = condition.getSequence() == screening.getSequence() } if discountable { break } } var fee : Money if discountable { var discountAmount = Money.ZERO switch movie.getMovieType() { case AMOUNT_DISCOUNT: discountAmount = movie.getDiscountAmount() case PERCENT_DISCOUNT: discountAmount = movie.getFee().times(movie.getDiscountPercent()) case NONE_DISCOUNT: break } fee = movie.getFee().minus(discountAmount).times(audienceCount) } else { free = movie.getFee() } return Reservation(custom, screening, fee, audienceCount) } } ReservationAgency
  • 45.
    ReservationAgency class ReservationAgency { funcreserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation { let discountable = mayDiscountable(screening) let fee = calculateFee(with: screening, discountable: discountable, audienceCount: audienceCount) return Reservation(screening, customer, audienceCount, fee) } }
  • 46.
  • 48.
  • 49.
    ➔ main() input() -> process()-> output() validate() format()save() ➔ - - - - -
  • 50.
    Client Employee calculatePay() monthlyBasePay() SalariedEmployee HourlyEmployee calculatePay() monthlyBasePay() calculatePay() monthlyBasePay() funccalculatePay(taxRate: Double) -> Double { if (hourly) { return calculateHourlyPay(taxRate) } return calculateSalariedPay(taxRage) }
  • 51.
    • : goto • : + ( ) • : /
  • 52.
  • 53.
  • 54.
    Main HL1 HL2 HL3 ML1ML2 ML3 ML4 ML5 ML6 LL1 LL1 LL3 LL4 LL5 LL6 LL7 LL8 LL9 LL10 LL11 LL12
  • 55.
    HL1 ML1 + Foo() <<Protocol>> AM + Foo() 호출을하는 모듈과 호출 받는 모듈 모두 소스 코드 의존성을 원하는 방향으로 설정할 수 있다. 소스 코드 의존성을 원하는 방향으로 관리할 수 있기 때문에 모듈, 컴포넌트 또는 배포가능한 단위로 서로 의존하지 않도록 컴파일할 수 있다. 결국 서로 독립적으로 배포를 할 수 있다. (배포 독립성)
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
    // / View ControllerModel Presenter View
  • 63.
    Model - View- Controller Controller : Coordination Model : Data View : Display / UIViewController NSObject UIView
  • 65.
    1. : ,? 2. : - ? 3. : ? 4. : , ? 5. : ? Point of View
  • 66.
    MVC : objc AppArchitecture
  • 67.
    MVVM + C :objc App Architecture
  • 68.
    VIPER : objc AppArchitecture
  • 69.
    3. 🧹 POP =Value Type + OOP + FP
  • 71.
    Classes Are Awesome •Encapsulation • Access Control • Abstraction • Namespace • Expressive Syntax • Extensibility ClassesClassesClassesTypes I can do all that with structs and enums. WWDC 2015 : POP
  • 72.
    POP = ValueType + OOP + FP
  • 73.
    class Ordered { funcprecedes(other: Ordered) -> Bool { fatalError("implement me!") } } class Number : Ordered { var value: Double = 0 override func precedes(other: Ordered) -> Bool { return self.value < (other as! Number).value } } OOP with Class
  • 74.
    protocol Ordered { funcprecedes(other: Self) -> Bool } struct Number : Ordered { var value: Double = 0 func precedes(other: Number) -> Bool { return self.value < other.value } } POP with struct
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
    class Screening { funcreserve(with customer: Customer, audienceCount: Int) -> Reservation { } } class Screening { private var movie : Movie private var sequence : Int private var whenScreened : DateTime func reserve(with customer: Customer, audienceCount: Int) -> Reservation { } } class Screening { private var movie : Movie private var sequence : Int private var whenScreened : DateTime func reserve(with customer: Customer, audienceCount: Int) -> Reservation { return Reservation(with: customer, screening: self, fee:calculateFee(audienceCount), audienceCount) } func calculateFee(int audienceCount) -> Money { return movie.calculateMovieFee(self).times(audienceCount) } } Screening
  • 85.
    class Movie { funccalculateMovieFee(screening: Screening) -> Money { } } class Movie { private let title : String private let runningTime : TimeInterval private let fee : Money private var discountConditions = Array<DiscountCondition>() private let movieType : MovieType private let discountAmount : Money private let discountPercent : Double func calculateMovieFee(screening: Screening) -> Money { } } Movie
  • 86.
    class Movie { enumMovieType { case AmountDiscount case PercentDiscount case NoneDiscount } private let title : String private let runningTime : TimeInterval private let fee : Money private var discountConditions = Array<DiscountCondition>() private let movieType : MovieType private let discountAmount : Money private let discountPercent : Double func calculateMovieFee(screening: Screening) -> Money { if isDiscountable(for: screening) { return fee.minus(calculateDiscountAmount()) } return fee } private func isDiscountable(for: Screening) -> Bool { return discountConditions.filter{ $0.isSatisfied(by: screening) }.count > 0 } } Movie
  • 87.
    class DiscountCondition { funcisSatisfied(by screening: Screening) -> Bool { } } class DiscountCondition { private let type : DiscountConditionType private let sequence : Int private let dayOfWeek : DayOfWeek private let startTime : Date private let endTime : Date func isSatisfied(by screening: Screening) -> Bool { if type == .period { return isSatisfiedByPeriod(screening) } return isSatisfiedBySequence(screening) } private func isSatisfiedByPeriod(_ screening: Screening) -> Bool { return dayOfWeek.equals(screening.whenScreened.dayOfWeek) && startTime.compare(to: screening.whenScreended.toLocalTime() <= 0) && endTime.isAfter(to: screening.whenScreended.toLocalTime() >= 0) } private func isSatisfiedBySequence(_ screening: Screening) -> Bool { return sequence == screening.sequence } } DiscountCondition
  • 88.
    class Screening { privatevar movie : Movie private(set) var sequence : Int private(set) var whenScreened : DateTime func reserve(with customer: Customer, audienceCount: Int) -> Reservation { return Reservation(with: customer, screening: self, fee:calculateFee(audienceCount), audienceCount) } func calculateFee(int audienceCount) -> Money { return movie.calculateMovieFee(self).times(audienceCount) } } Screening & DiscountCondition class DiscountCondition { enum DiscountConditionType { case Sequence, Period } private let type : DiscountConditionType private let sequence : Int private let dayOfWeek : DayOfWeek private let startTime : DateTime private let endTime : DateTime //…
  • 89.
  • 90.
    class PeriodCondition { privatelet dayOfWeek : DayOfWeek private let startTime : Date private let endTime : Date init(dayOfWeek: DayOfWeek, startTime: DateTime, endTime: DateTime) { self.dayOfWeek = dayOfWeek self.startTime = startTime self.endTime = endTime } func isSatisfied(by screening: Screening) -> Bool { return dayOfWeek.equals(screening.whenScreened.dayOfWeek) && startTime.compare(to: screening.whenScreended.toLocalTime() <= 0) && endTime.isAfter(to: screening.whenScreended.toLocalTime() >= 0) } } PeriodCondition - SequenceCondition class SequenceCondition { private let sequence : Int init(with sequence: Int) { self.sequence = sequence } func isSatisfied(by screening: Screening) -> Bool { return sequence == screening.sequence } }
  • 91.
  • 92.
    class Movie { enumMovieType { case AmountDiscount case PercentDiscount case NoneDiscount } private let title : String private let runningTime : TimeInterval private let fee : Money private let movieType : MovieType private let discountAmount : Money private let discountPercent : Double private var periodConditions = Array<PeriodCondition>() private var sequenceConditions = Array<SequenceCondition>() func calculateMovieFee(screening: Screening) -> Money { if isDiscountable(for: screening) { return fee.minus(calculateDiscountAmount()) } return fee } private func mayPeriodConditions(with screening: Screening) -> Bool { return periodConditions.filter{ $0.isSatisfied(by: screening) }.count > 0 } private func maySequenceConditions(with screening: Screening) -> Bool { return sequenceConditions.filter{ $0.isSatisfied(by: screening) }.count > 0 } private func isDiscountable(for: Screening) -> Bool { return mayPeriodConditions(with: screening) || maySequenceConditions(with: screening) } Movie
  • 93.
    :Sequence Condition :Period Condition :Movie :Discount Condition DiscountCondition protocol DiscountCondition { funcisSatisfied(by screening: Screening) -> Bool } class PeriodCondition : DiscountCondition { } class SequenceCondition : DiscountCondition { }
  • 94.
    class Movie { enumMovieType { case AmountDiscount case PercentDiscount case NoneDiscount } private let title : String private let runningTime : TimeInterval private let fee : Money private let movieType : MovieType private let discountAmount : Money private let discountPercent : Double private var discountConditions = Array<DiscountCondition>() func calculateMovieFee(screening: Screening) -> Money { if isDiscountable(for: screening) { return fee.minus(calculateDiscountAmount()) } return fee } private func isDiscountable(for: Screening) -> Bool { return discountConditions.filter{ $0.isSatisfied(by: screening) }.count > 0 } Movie
  • 95.
    Protected Variations Movie -title +calculateMovieFee() Screening <<interface>> DiscountCondition +isSatisfiedBy() SequenceCondition + isSatisfiedBy() PeriodCondition + isSatisfiedBy() + reserve() movie discountConditions
  • 96.
    Movie Movie -title +calculateMovieFee() Screening <<interface>> DiscountCondition + isSatisfiedBy() SequenceCondition +isSatisfiedBy() PeriodCondition + isSatisfiedBy() + reserve() movie discountConditions Percent DiscountMovie None DiscountMovie Amount DiscountMovie #calculateDiscountAmount() #calculateDiscountAmount() #calculateDiscountAmount()
  • 97.
    anObject foobar[ ]; instancemethod self anObject action message[ ] anObject.foobar()
  • 98.
    , , self anObject 1. message[] operation method2. 3.
  • 99.
  • 100.
    before class ReservationAgency { funcreserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation { let movie = screening.getMovie() var discountable = false for condition in movie.getDiscountConditions() { if condition.getType() == .Period { discountable = screening.whenScreened().getDayOfWeek().equals(codition.getDayOfWeek()) && condition.getStartTime().compareTo(screening.whenScreend().toLocalTime()) <= 0 && condition.getEndTime().compareTo(screening.whenScreend().toLocalTime()) <= 0 && } else { discountable = condition.getSequence() == screening.getSequence() } if discountable { break } } var fee : Money if discountable { var discountAmount = Money.ZERO switch movie.getMovieType() { case AMOUNT_DISCOUNT: discountAmount = movie.getDiscountAmount() case PERCENT_DISCOUNT: discountAmount = movie.getFee().times(movie.getDiscountPercent()) case NONE_DISCOUNT: break } fee = movie.getFee().minus(discountAmount).times(audienceCount)
  • 101.
    after class ReservationAgency { funcreserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation { let fee = screening.calculateFee(audienceCount) return Reservation(custom, screening, fee, audienceCount) } }
  • 102.
    exception class PeriodCondition :DiscountCondition { private let dayOfWeek : DayOfWeek private let startTime : Date private let endTime : Date init(dayOfWeek: DayOfWeek, startTime: DateTime, endTime: DateTime) { self.dayOfWeek = dayOfWeek self.startTime = startTime self.endTime = endTime } func isSatisfied(by screening: Screening) -> Bool { return dayOfWeek.equals(screening.whenScreened.dayOfWeek) && startTime.compare(to: screening.whenScreended.toLocalTime() <= 0) && endTime.isAfter(to: screening.whenScreended.toLocalTime() >= 0) } } class PeriodCondition : DiscountCondition { private let dayOfWeek : DayOfWeek private let startTime : Date private let endTime : Date // func isSatisfied(by screening: Screening) -> Bool { return screening.isDiscountable(dayOfWeek: dayOfWeek, startTime: startTime, endTime: endTime) } } extension Screening { func isDiscountable(dayOfWeek: DayOfWeek, startTime: DateTime, endTime: DateTime) -> Bool { return dayOfWeek.equals(whenScreened.dayOfWeek) && startTime.compare(to: whenScreended.toLocalTime() <= 0) && endTime.isAfter(to: whenScreended.toLocalTime() >= 0) } }
  • 103.