- Lv.1에서 만든
Calculator
클래스에 나머지 연산(%
) 추가하고 결과를 출력- 오류가 날 수 있는 예외처리 상황에 대해 고민해보고, 구현하기
AddOperation
,SubstractOperation
,MultiplyOperation
,DivideOperation
클래스를 만들고 클래스 간의 관계를 고려하여 Calculator 클래스와 관계 맺기- Calculator 클래스의 내부 코드를 변경 (관계를 맺은 후 필요하다면 별도로 만든 연산 클래스의 인스턴스를 Calculator 내부에서 사용)
- 클래스의 단일 책임 원칙에 대해 생각하기
어제 코드에 나머지 연산을 추가했다. 메소드를 호출할 때는 firstNumber
, secondNumber
파라미터에 숫자를 받지만, 메소드 내에서 연산 시 a
와 b
로 간결하게 정의하고 싶어 전달인자 레이블
을 사용했다.
나머지 연산의 경우 정수여야만 하기 때문에, 파라미터와 리턴 타입을 Int
로 정의해야 했다.
객체 지향 프로그래밍에서 단일 책임 원칙(Single Responsibility Principle)이란 모든 클래스는 하나의 책임(기능)만 가지도록 설계해야 함을 일컫는다. 클래스가 제공하는 모든 기능들이 해당 클래스의 책임에 부합해야한다는 것이다. 이는 설계의 응집력을 높이고자 함이다.
이것을 고려했을 때, 과제의 의도는 각 연산 클래스가 해당 연산 만을 담당하도록 하라는 것 같다.
다만, 3번 항목의 Calculator 클래스와 관계
를 맺으라는데 이게 정확히 무슨 의미인지 잘 모르겠다. 4번에 관계를 맺은 후
인스턴스를 생성하여 사용하라고 하는데, 나는 인스턴스 생성하여 사용하는 자체가 관계 형성이라고 생각했는데 글은 그 외의 별도 조치가 필요하다는 말로 읽혀 그게 뭔지 모르겠어서 혼란스럽다. 일단 내가 할 수 있는 범위 내에서 코드 수정을 진행했다.
Calculator
내의 사칙연산 함수는 일단 주석처리 하고, 각 연산 클래스로 메소드들을 옮긴 뒤 Calculator
에서 인스턴스를 통해 각 연산 메소드에 접근하여 계산 기능을 수행했다.
구현 요구사항에 대해 100% 이해한 게 아니라서 그런지, 코드를 업데이트 하고도 뭔가 부족한 기분이 계속 든다. Lv.4까지 진행하다보면 생각이 더 확장되면서 이해할 수 있을지도 모르겠다. 오늘은 일단 여기까지.
- 버튼 19개를 넣을 영역인
UIView
ButtonArea의 높이를 전체 높이의 60%로 지정Button
의width
=height
= (화면 전체 너비 - 양 끝으로부터의 간격 16.0 x 2 - 버튼 간 간격 8.0 x 3) / 4
Combine
을 활용해 SceneDelegate
에서 windowScene
의 화면 size를 Publisher
에 주입한다.
Publisher
인 화면 size 변수를 구독
하여 각 UI의 size를 지정한다. 코드는 아래와 같다.
import Combine
// view model에 생성한 Publisher
final class MainViewModel {
// shared 선언
static let shared = MainViewModel()
// screen size를 Tuple로 갖는 Subject(Combine Publisher)
// default size : iPhone 16 Pro : 402 * 874
let screen = CurrentValueSubject<(width: CGFloat, height: CGFloat), Never>((402, 874))
}
// SceneDelegate 내 willConnectTo 함수 코드
guard let windowScene = scene as? UIWindowScene else { return }
// screen 크기 전송 : send 메소드
let vm = MainViewModel.shared
vm.screen.send((width: windowScene.screen.bounds.width, height: windowScene.screen.bounds.height))
// MainViewController, Button class에서 view model의 screen을 구독해 각 UI 크기 지정
class MainViewController: UIViewController {
private var cancellables = Set<AnyCancellable>()
let vm = MainViewModel.shared
// ... //
// view did load에 호출되도록 한다.
private func setButtonAreaHeight() {
// ButtonArea 높이 = 화면 전체 높이의 60%
vm.screen
.sink { [weak self] screen in
self?.buttonArea.heightAnchor.constraint(equalToConstant: screen.height * 0.6).isActive = true
}
.store(in: &cancellables)
}
}
class Button: UIButton {
private setButtonSize() {
// button width = (screen.width - 16 * 2 - 8 * 3) / 4
vm.screen
.sink { [weak self] screen in
self?.widthAnchor.constraint(equalToConstant: (screen.width - 56) / 4).isActive = true
self?.heightAnchor.constraint(equalToConstant: (screen.width - 56) / 4).isActive = true
}
.store(in: &cancellables)
}
}
size가 적용되어 button
이 적절하게 배치된 것을 확인할 수 있다.
오토레이아웃이 잘 적용되었는지 iPhone 16 Pro Max로 확인했는데, 제대로 적용되지 않아 있었다.
원인은
Publisher Type
을CurrentValueSubject
로 하여 기본값에 iPhone 16 Pro 크기를 지정해둔 데에 있었다.
send
동작 시점과sink
동작 시점이 맞지 않나보다. (이걸 100% 이해하기 위해서는Combine
에 대한 공부가 더 필요하겠다.
기본값이 없는
PassthroughSubject
로 바꾸어windowScene
을 통해 화면 크기가send
된 뒤에 레이아웃이 잡히도록 하였다.
let screen = PassthroughSubject<(width: CGFloat, height: CGFloat), Never>()
Pro Max에도 오토레이아웃이 제대로 적용된 것을 확인할 수 있다.
버튼이 완전한 원이 아닌데, 추후에 cornerRadius
값도 구독을 통해 지정해야겠다.
나머지 연산 저렇게도 되는구나... 머시ㄸr