2022-09-28 수요일
3주차
1교시, 2교시
어제자 복습
어제 했던 객체지향 프로그래밍을 복습하기 위해 BankAccount 코드를 다시 한번 더 써보는 중이다.
🦁 내부 문법을 정말 잘 모르겠다! 어떡하지 나 개발 못하는 걸까..?
→ NO, 다 외우려고 들지말자. 익숙해지는 것이 중요하다.
3교시, 4교시, 5교시, 6교시
Swift
서브클래싱과 익스텐션
목표
- 앞서 배운 것
- 객체지향 프로그래밍의 개념
- 새로운 클래스를 생성하고 작업하는 방법
- 이번에 배울 것
- 상속
- 서브 클래싱
- 익스텐션→ 유용하다. 편하다!
✔️ Inheritance, 상속
정의
예전에는 부모-자식 관계로 설명을 많이 했는데 요즈음엔 super- sub로 설명을 더 많이 한다.
상속 → 현실 세계의 관점을 프로그래밍으로 가져온 것
- 클래스에 어떤 특성(예. 메서드와 프로퍼티)을 정의할 수 있고, 그 클래스를 상속받은 다른 클래스를 생성할 수 있게 해준다.
- 상속된 클래스(sub)는 상속한 클래스(super)의 모든 기능을 상속받고, 자신만의 기능을 추가하게 된다.
- 계층구조(class hierarchy)
- base class, child class, sub class
- prarent class, super class
상속관계 만들기
- 밖(클래스 밖)에서는 BankAccount라는 타입으로 부를 수 있다.
class BankAccount {
var accountBalane: Float
var accountNumber: Int
init(number: Int, balance: Float) {
accountNumber = number
accountBalance = balance
}
func displayBalance() {
print("Number \\(accountBalance)")
print("Current balance is \\(accountNumber)")
}
}
BankAccount를 상속받는 저축계좌로 사용할 SavingsAccount 계좌를 만들어보자.
- BankAccount의 내용을 모두 복사해서 SavingAccount 클래스에 넣고, 필요한 기능을 넣으면 완성이 된다.
- 근데 효율적이지 못하다. 이러면 BankAccount의 내용이 바뀌었을 때 일일이 또 바꾸어줘야 한다. 그리고 아주 많은 서브클래스가 있다면… 사람의 실수가 발생할 가능성이 높아진다.
BankAccount의 내용을 복사하여 만들 수도 있지만, SavingsAccount를 BankAccount의 하위 클래스로 만드는 것이 더욱 효율적이다.
- BankAccount를 상속받은 SavingsAccount는 BankAccount의 모든 기능을 상속받고, 필요한 기능을 추가하여 확장할 수 있다.
그럼 한번 상속관계로 만들어보자.
class SavingsAccount: BankAccount {
}
프로토콜을 채택할 때도 위 코드처럼 썼다! 클래스명 옆에 상속받을 클래스명을 적어준다.
아직 SavingAccount 내부에 어떠한 인스턴스 변수나 메서드를 추가하지 않았지만, SavingAccount 클래스는 BankAccount 클래스의 모든 메서드, 프로퍼티를 상속받았음을 알 수 있다.
- protocol
- 이런거 만들어줘! 내부의 구현된 코드는 없음. 채택한 프로토콜대로 기능을 구현해주어야 한다.
- super class
- 내부의 구현된 코드가 있다. 필요한 것만 추가적으로 만들어주면 된다.
✔️ Subclassing, 서브 클래싱
Subclassing is the act of basing a new class on an existing class.
하위 클래스의 기능 확장하기
하위 클래스에 필요한 기능을 따로 추가해준다.
- 슈퍼 클래스의 프로퍼티, 메소드는 다시 적을 필요가 없다. 상속받았으므로 그냥 사용할 수 있다. (프로토콜이었다면 구현부를 작성해줘야 함)
BankAccount 클래스를 상속받은 하위 클래스 SavingAccount에 프로퍼티와 메서드를 추가하였다.
class SavingsAccount: BankAccount {
var interestRate: Float = 0.0
func calculateInterest() -> Float {
return interestRate * accountBalance
}
}
Overriding
A subclass can provide its own custom implementation of an instance method, type method, instance property, type property, or subscript that it would otherwise inherit from a superclass.
상속받은 메서드를 새롭게 구현하기
상속을 사용하면 슈퍼 클래스에서 사용하는 메서드를 그대로 사용할 수 있다.
그러나 서브 클래스에서 더 정확한 기능을 제공하기 위해 수정이 필요할 수 있다. 그럼 슈퍼 클래스에서 사용하는 기능을 무시하고 새로 만들어야 할까? 그래도 된다. 하지만… 더 좋은 방법이 있다.
상속 받은 메서드를 오버라이드(override)하여 하위 클래스내에서 새로운 버전의 메서드를 만드는 것이다.
- 오버라이딩을 할 때 지켜야 할 규칙
- 매개변수, 타입 등 모양이 완전히 일치해야 한다.
- 반환 타입도 같아야 한다.
예시)
class SavingsAccount: BankAccount {
var interestRate: Float = 0.0
func calculateInterest() -> Float {
return interestRate * accountBalance
}
override func displayBalance() { // 부모 클래스에 있는 displayBalance 함수를 override.
print("Number \\(accountNumber)")
print("Current balance is \\(accountBalance)")
print("Prevailing interest rate is \\(interestRate)")
}
}
- override 키워드가 앞에 붙은 displayBalance 메서드의 새로운 버전을 SavingsAccount 클래스에 선언한다.
그런데 메서드를 override 하면, 슈퍼클래스의 메서드에서 썼던 내용도 다 지워진다. 어카지? 난 그 메서드 내용도 쓰고 싶은데!
- super라는 키워드를 사용하면 super 클래스에서 썼던 내용을 가져올 수 있다.
- 코드의 중복을 없앨 수 있다~
super.displayBalance()
실제 구현부에 넣어보면 아래와 같다.
class SavingsAccount: BankAccount {
var interestRate: Float = 0.0
func calculateInterest() -> Float {
return interestRate * accountBalance
}
override func displayBalance() {
super.displayBalance() // 부모 클래스의 displayBalance 함수 호출
print("Prevailing interest rate is \\(interestRate)")
}
}
하위 클래스 초기화하기
슈퍼 클래스의 초기화함수를 쓸 수 있다.
init은 func 키워드가 없다. 그래서 오버라이딩이라고 일컫지도 않는다…!
- 단, 하위 클래스의 init 매개변수가 상위 클래스의 init 매개변수와 같으면 override 키워듣 붙여줘야 한다.
class BankAccount {
...
init(number: Int, balance: Float) {
accountNumber = number
accountBalance = balance
}
...
}
class SavingsAccount: BankAccount {
...
var interestRate: Float
init(number: Int, balance: Float, rate: Float) {
interestRate = rate
super.init(number: number, balance: balance)
}
...
}
- 매개변수가 추가되면 override라고 하지 않는다.
- 초기화 과정에서 발생할 수 있는 잠재적인 문제를 피하기 위해 상위 클래스의 init 메서드는 항상 하위 클래스의 초기화 작업이 완료된 후에 호출되도록 해야 한다.
- 아니어도 작동은 되지만… 습관적으로 위와 같이 진행하자.
아래와 같이 코드를 쓸 수 있다.
let savings = SavingAccount(number: 12311, balance: 600.00, rate: 0.7)
print(savings.calcurateInterest())
savings1.displayBalance()
✔️ Extension, 익스텐션
Extensions add new functionality to an existing class, structure, enumeration, or protocol type.
필요한 기능이 있는데 상위 클래스에 없으면 하위 클래스를 또 상속받아서 매번 기능을 만들어줘야 하나? 매번 상속을 해줘야 하나? 노놉.
익스텐션을 사용하자. 익스텐션은 메서드만 가능하고 프로퍼티는 불가능하다. (연산o, 저장x)
Swift 클래스에 새로운 기능을 추가하는 또 다른 방법은 extension을 사용하는 것이다.
- 익스텐션은 하위 클래스를 생성하거나 참조하지 않고 기존 클래스에 메서드, 초기화, 그리고 연산 프로퍼티와 서브스크립트 등의 기능을 추가하기 위해 사용될 수 있다.
This includes the ability to extend types for which you don’t have access to the original source code (known as retroactive modeling).
extension ClassName {
// 새로운 기능을 여기에 추가한다.
}
어? 프로퍼티는 안된다매.
Nono. 정확하게는 저장 프로퍼티가 안된다. 연산 프로퍼티는 익스텐션으로 추가가 가능하다.
- Swift 언어와 iOS SDK 프레임워크에 내장된 클래스에 기능을 추가할 때 익스텐션을 이용하면 매우 효과적일 수 있다.
예제) 표준 Double 클래스에 제곱값을 반환하는 프로퍼티와 세제곱 값을 반환하는 프로퍼티를 추가하고자 한다고 하자.
이 기능은 다음의 익스텐션 선언부를 이용하여 추가할 수 있다.
extension Double {
var squared: Double { // getter
return self * self
}
var cubed: Double {
return self * self * self // getter
}
}
여기서 self는 숫자 그 자체 값이라고 보면 된다.
Double 클래스에 2개의 새로운 연산 프로퍼티를 가지도록 확장했다. 이제 아래와 같이 사용할 수 있다!
let myValue: Double = 3.0
print(myValue.squared)
// 9.0
- 주목할 점 : myValue 상수를 선언할 때 Double형이 되도록 선언하고 익스텐션 프로퍼티를 사용했다는 것이다.
실제로 프로퍼티는 하위 클래스를 사용하는 것이 아니라 익스텐션으로 추가한 것이므로, Double 값에서 이 프로퍼티에 직접 접근할 수 있다.
let myValue: Double = 3.0
print(myValue.squared)
print(3.0.squared)
print(6.0.squared)
근데 이렇게 하면 소수점과 dot annotation이 헷갈리니까 주의해야한다.
String에서도 이런식으로 사용할 수 있다. 근데 헷갈리니까 머..
📔 요약
- 상속
- 새로운 클래스가 기존의 클래스로부터 파생되어 새로운 기능이 추가되도록 하는 방법
- OOP에서 객체의 재사용성 개념을 확장시켜 준다.
- 기존 클래스를 새로운 하위 클래스의 기본형처럼 사용할 수 있게 해준다. (하위 클래스에 새로운 기능을 추가하지 않을 경우)
- 익스텐션
- 하위 클래스를 생성하지 않고도 기존의 클래스에 기능을 추가할 수 있는 방법 제공
남들이 만들어둔 좋은 오픈소스, 코드가 있다면 그것을 활용하는 것이 좋다.
가져다 쓸 수 있으면 쓸 수 있다. 그러나 어떻게 돌아가는지는 알아야겠징? 쿠쿡. 그러다가 오픈소스 오류잡고, 문서 번역하고.. 어쩌구 하다가 취업될수도? 쿠쿠쿡.
✏️ 적용해보기
계좌 정보를 담는 클래스를 만들어보자.
// 계좌 정보를 담는 클래스를 만들어라
class BankAccount {
// 프로퍼티
// 1. 은행 계좌 번호
var accountNumber: Int = 0
// 2. 계좌에 남은 잔고
var accountBalance: Float = 0.0
// 3. 수수료
let fees: Float = 5.00
// 4.현재의 잔액에서 수수료를 뺀 급액
// 연산 프로퍼티 - 밖에서 보면 프로퍼티지만 안에서는 get과 set의 메서드(함수)들
var balanceLessFees: Float {
get {
return accountBalance - fees
}
set {
accountBalance = newValue + fees
}
}
// 메서드 - method
// 초기화 - 계좌 번호와 잔고를 받아서 시작
init(number: Int, balance: Float) { // (Int, Float) -> Void
accountNumber = number
accountBalance = balance
}
deinit {
print("인스턴스가 제거됩니다")
}
// 계좌 정보를 출력하기
func displayBalance() {
print("Number \\(accountNumber)")
print("Current balance is \\(accountBalance)")
print("Current balanceLessFees is \\(balanceLessFees)")
}
// BankAccount가 최대로 받을 수 있는 금액
class func getMaxBalance() -> Float {
return 100000.00
}
}
class SavingsAccount: BankAccount {
// 이자율 프로퍼티 추가
var interestRate: Float = 0.0254
// 초기화 - 계좌 번호와 잔고를 받아서 시작
// 만약 Sub 클래스 어디에도 초기화 함수(init) 없으면 Super 클래스의 init을 끌어와 사용
// Super 클래스의 초기화 함수와 타입이 같기 때문에 override 적음
override init(number: Int, balance: Float) { // (Int, Float) -> Void
interestRate = 0.3
super.init(number: number, balance: balance)
}
// Super 클래스에는 없던 함수 타입의 초기화 함수이기 때문에 override 안써줌
init(number: Int, balance: Float, rate: Float) { // (Int, Float, Float) -> Void
interestRate = rate
super.init(number: number, balance: balance)
}
// 이자 구하는 메소드-함수 추가
func calculateInterest() -> Float {
return interestRate * accountBalance
}
// 계좌 정보를 출력하기
override func displayBalance() { // () -> Void
// Super 클래스인 BankAccount의 내용과 겹치니까
// print("Number \\(accountNumber)")
// print("Current balance is \\(accountBalance)")
// print("Current balanceLessFees is \\(balanceLessFees)")
super.displayBalance()
print("Current interestRate is \\(interestRate)")
}
}
let pocketMoney: Float = 124.0
// 우리가 입금할 금액이 어떤계좌가 만들어지든 입금 가능한 금액일 때에만...
if pocketMoney < SavingsAccount.getMaxBalance() {
// BankAccount 타입의 인스턴스(객체) 생성
let account: SavingsAccount = SavingsAccount(number: 12312312345, balance: pocketMoney)
print("이자: \\(account.calculateInterest())")
account.displayBalance()
} else {
print("금액이 계좌의 한도를 초과했습니다")
}
7교시, 8교시
회고조안에서 조원들과 함께 Playground 해결하기 - 실전편
'외부활동 > 멋사 앱스쿨 1기' 카테고리의 다른 글
[TIL] 22-09-30: Open Source, Git/GitHub (0) | 2022.10.03 |
---|---|
[TIL] 22-09-29: Swift (Struct, Property Wrapper, Collection - Array, Dictionary) (0) | 2022.10.01 |
[TIL] 22-09-27: Swift (객체지향 프로그래밍 기초) (0) | 2022.09.27 |
[TIL] 22-09-26: Swift (함수, 메서드, 클로저) (0) | 2022.09.26 |
[TIL] 22-09-23: 오픈소스, Swift(반복문, 조건문) (0) | 2022.09.24 |