당일 진행한 코드리뷰(19:10~21:00)에 관한 기록입니다.
Q:didSet을 위해 name 프로퍼티를 { get set }으로 선언하신 것일지 궁금합니다..!
프로토콜을 채택한 객체 내에서 name을 따로 연산 프로퍼티로 사용하거나 하시진 않은 것 같아 여쭈어봅니다..!
혹시 위와 같은 이유라면, didSet은 (예외를 제외하고) 저장 프로퍼티에서만 사용할 수 있기 때문에 { get }으로만 선언해주었어도 좋을 것 같아요☺️
프로퍼티 선언 시 { get set }으로 선언을 하면 객체에서 채택 시 해당 프로퍼티는 var로만 선언 가능하여 구현의 자유도가 떨어진다고 합니다.(let으로 선언 불가능) 좀더 자유로운 구현을 위해 제한 사항을 덜어주는 게 어떨까 싶어요!
저는 아무생각없이(...) { get }으로만 선언을 하고 있었는데 영빈님 코드 덕분에 자세히 학습하는 기회가 되었습니다☺️
A:아, var name: String {get, set}으로 설정한 이유는
{get,set}, {get}은 프로토콜 자체를 변수로 선언해서 사용할 경우에 차이점이 나타납니다.
테스트 시에는 아래와 같은 코드도 작성했었는데,
var otherRobot: Introducible = Robot(name: "다른로봇")
이렇게 사용할 시 에러가 발생해서 get,set 둘 다 선언했습니다.
물론, 현재 코드에서는 프로토콜 자체 변수 선언하는 구문을 빼고 업로드해서 {get}만 사용해도 문제 없습니다 !
질문, 설명 감사해요.
Q:선택 사항인데도 실제 자동차처럼 부가 기능을 많이 구현하신 점이 인상깊습니다ㅎㅎㅎ
refuel()함수에 직접적으로 print("연료가 가득찼습니다."가 바로 출력되도록 하지 않고 isFull 함수를 따로 만드신 이유가 있으실지 궁금합니당!
A:두 메소드를 하나의 메소드로 만들어서 제공할 수 있으나, 접근 제어자를 달리하여 인스턴스 선언 후 외부 호출 가능 유무를 달리하고자 했습니다.
선언을 확인해보시면 두 메소드는
private func isFull(), public func refuel() 입니다.
isFull은 private으로 선언되어 외부 인스턴스에서 직접 참조가 불가능하여, 이를 사용하기 위해서는 public으로 선언한 refuel을 이용할 때만, 조건부로 사용할 수 있도록 구현했습니다.
Q:아하.. 접근 제어자를 확인하지 못했네요ㅠㅠ! 설명 감사드립니다 어떤 의도이신지 이해했습니다!!
다만 현재 코드에서 refuel()에서 이미 fuel >= 100 조건을 체크한 다음 isFull()에서 다시 동일 조건인 fuel >= 100을 확인하고 있는 것 같습니다!
refuel()에서 isFull()을 실행한 다음 조건문으로 들어가는 건 어떠실지 말씀드려봅니당..ㅎㅎㅎ
A:메소드 구성할 때, 조건을 크게 신경안쓰고 동작만 하게 만들자 생각했더니 ....
조금 더 수정했습니다. 의견 감사해요.
Q:인스턴스 변수명은 네이밍 컨벤션에 따라 소문자로 선언해주셨으면 좋았을 것 같아요! var b: B?
A:매번 작성하던 방식이랑 달라서 헷갈리네요.. 짚어주셔서 감사합니다.
직접 코드를 작성해보며, 문제를 풀다보니 비슷하게 느껴지는 부분이 있었다. 프로토콜의 확장(Protocol Extension)과 클래스의 상속(Class Inheritance)이다.
본래 프로토콜은 '규약'이라고 생각할 수 있지만, Extension을 사용하면 선언한 함수 자체를 정의할 수 있으므로 상속과 같은거 아닌가라는 생각을 하게 되었다. 또한, Extension으로 정의한 프로토콜을 채택하여 사용하는 타입에서 함수를 오버라이딩하는 기능도 제공되기에 "왜 굳이 비슷한 두 가지의 기능을 제공할까?" 라는 생각이 들었다.
차이점으로는
프로토콜과 달리 상속은 다중 상속이 불가능하다
상속은 클래스만을 대상으로 바라본다.
프로토콜은 수평적 관계, 상속은 수직적 관계이다.
그러면 왜 상속을 왜 사용해?
프로토콜은 저장 프로퍼티를 지원하지 못한다.
기본적으로, 오버라이딩을 지원하지만 상속만큼 매끄럽지 않다.
UIKit 내부의 동작의 설계가 상속을 사용하도록 구현되어 있다. (UIViewController, UIView ... 등) 기본적인 설계 자체가 상속을 이용하도록 구현되어있다.
큰 틀은 상속으로 잡고, 세부 기능들은 프로토콜로 만들어두는 것이 가장 좋은 방법이지 않을까 생각한다
React를 사용하며 map을 사용했지만, 깊이있게 이걸 왜 사용해야하지? 라는 고민에 빠져본 적은 없었다. 반복문을 사용해야 하는 구간에서 코드를 보다 간결하게 작성할 수 있어 이점이 있다는 사실만으로 사용했던 기억이 있다. swift를 공부하며 "고차 함수는 동작 속도가 더 빠른 것도 아니며 메모리 사용이 기존 코드보다 적은 것도 아닌데 왜 사용할까?"에 관한 의구심을 가지고 공부한 내용을 기록해보고자 한다.
사실, 지금은 크게 체감되지 않는다. 프로젝트를 실제로 진행하며 코드의 길이가 스크롤 몇번으로 해결되지 않을 정도로 길어지고, 규모가 커지는 상황이 되었을 때, 직접 사용하며 몸으로 체득하는 것이 더 유리하지 않을까 생각한다.(React에서도 map과 filter는 몸으로 체득했기에)
let nums = [1, 2, 3]
// [for-in] 직접 구현 (명령형)
var forRes: [String] = []
for n in nums { forRes.append("\(n)") }
// [map] 구현 (선언형)
let mapRes = nums.map { "\($0)" }
let nums = [1, 2, 3, 4]
// [for-in] 직접 구현
var forRes: [Int] = []
for n in nums { if n % 2 == 0 { forRes.append(n) } }
// [filter] 구현
let filterRes = nums.filter { $0 % 2 == 0 }
let nums = [1, 2, 3]
// [for-in] 직접 구현
var sum = 0
for n in nums { sum += n }
// [reduce] 구현
let total = nums.reduce(0, +)
let numbers = [1, 2, 3, 4, 5]
numbers.forEach { num in
if num == 3 {
return
}
print("숫자: \(num)")
}
결과 :
숫자: 1
숫자: 2
숫자: 4
숫자: 5
// 주로 다른 고차함수와 체이닝하여 사용
let res = nums.enumerated().map { (idx, val) in "\(idx): \(val)" }
매일 열심히 공부하고 운동하는 것은 좋지만, 컨디션 관리는 그 중 가장 중요한 것이다. 감기에 걸려서 조금은 힘든 한 주였지만, 공부를 진행하며 기억해야 할 내용들이 제법 많아서 더 집중해서 참여했다. 강의도 한번을 듣는다고 완전히 그 내용을 숙지할 수는 없다. 한 순간에 모든 내용을 이해할 순 없고, 헷갈리는 부분들도 반복하다보면 언젠가 분명 이해할 수 있는 날이 온다. 또한, 강의 내용에 빈약한 부분이 있다면 튜터님께 얘기를 하거나 검색을 통해 필요 개념들을 더 공부한다. 하루 10시간씩, 주에 50시간 매 순간을 집중한 상태라고 얘기하는 것은 거짓말이지만, 그럼에도 시간을 허투루 보내지않고 집중하기 위해 노력했다는 것은 진실이다. 한 주 동안 가장 기억에 남는 것은, 코드 리뷰이다. 코드 리뷰를 통해 본인이 생각했던 방법 외의 다른 방법에 관해 들을 수 있고 하나의 문제를 여러 관점으로 의견을 나눌 수 있다는 점이 좋았다. 팀원들의 의견 하나하나에 귀를 기울이고 본인과 다른 부분들에 대한 의견을 흡수해나가면 그만큼 성장 속도가 올라갈 것이라 생각한다. 혼자서 공부하며 이해가 되지 않는 부분을 지속적으로 쳐다보는 거에 비하면, 함께하는 팀원이 있고 의견을 나눌 수 있는 사람들이 있다는 점이 얼마나 좋은가.