https://github.com/JinkownHong/CalculatorWithTutor.git
GitHub - JinkownHong/CalculatorWithTutor
Contribute to JinkownHong/CalculatorWithTutor development by creating an account on GitHub.
github.com
튜터님 강의를 다시보며 코드를 다시 작성해보고, 이해가 필요한 부분들은 추가적으로 주석을 달아 작성해보았다.
자세한 내용은 위 Github와 commit 내역, Readme, 주석을 함께 참고해보면 좋을 것 같다.
계산기를 만들며 가장 큰 소득은 객체지향을 이해하며 코드를 작성하는 것이다.
객체지향 관련 내가 이해한 것 + 구글에 찾아본 것 + 튜터님께 배운 내용을 혼합하여 아래 자세히 풀어보려 한다.
객체지향
객체지향 언어란 객체를 만들고 객체를 사용하는 프로그래밍 방법이다.
다시 말해, 프로그램을 그저 데이터와 처리 방법으로 나누는 것이 아니라 프로그램을 다수의 “객체”를 만들고, 서로 상호작용을 통해 만들어지는 방식이다.
프로그래밍은 궁극적으로 문제를 해결하는 것이 목적인데, 객체지향은 문제를 해결학 위한 하나의 방법, 도구이다.그러면 객체지향으로 문제를 어떻게 해결할 수 있을까?비유를 통해 설명하자면,절차지향 프로그래밍은 한 명의 통솔하에 여러 명에게 일을 시켜 순차적으로 문제를 해결해나가는 것이라고 한다면객체지향 프로그래밍은 목표를 이루기 위해 각자 잘하는(역할에 책임을 가진) 것이 있는 여러 등장인물 (객체를 의미)들이 서로 대화하고 협력하여 문제를 해결해나가는 것을 의미한다.
계산기 속 객체지향
위 과제로 구현했던 계산기로 예시를 들어보자.LV2 와 LV3의 차이점은, 객체지향 프로그래밍을 활용했는지 여부이다.객체를 2개에서 3개로 늘렸는데 사실 객체를 늘리면서 어떤 게 나아졌는지 와닿지 않을 수 있으나, 요구사항이나 각 객체가 하는 일이 비교적 단순하기 때문이다.실제로 현업에서 프로그래밍을 할 때 복잡한 기능들을 구현해야 하고,많은 역할들이 필요하기 때문에 계산기를 구현하며 객체 지향적 사고와 전체적인 구조를 이해하는 것이 중요하다.
객체 간 의존성이란?
위의 계산기를 참고하여,
operator class 에 요청을 보내는 Calculator 객체를 Client 객체라고 칭한다.
즉 Calculator 객체는 각 Operator 객체를 의존한다.
그렇다면 의존성이 왜 중요할까?
의존성은 변경을 전파하는 최대 원인 제공자이자 변경의 유연성을 방해하기 때문에
지속적으로 기능 개선, 코드 구조개선, 시스템 변화 등으로 코드 변경이 잦을 수 밖에 없는 프로그래밍 언어에서는 중요할 수 밖에 없다.
객체지향 프로그래밍에서 객체들은 서로 협력하는 존재이기 때문에 의존성은 절대 빠질 수 없으며, 변경의 전파를 받지 않기 위해서는 전체적인 구조나 작동 방식을 이해하는 것이 중요하다.
- 추상적인 것 / 구체적인 것 == 잘 안바뀌는 것 / 바뀔 가능성이 높은 것 == 높은 수준의 객체 / 낮은 수준의 객체
- 클라이언트 객체가 구체적인 것을 의존하면(의존한다는 것은 존재를 알게되는 것을 의미) 변경의 전파를 받을 확률이 높아진다.
-> 변경의 유연한 프로그래밍에 어려움을 겪을 수 있다.
- 클라이언트 객체가 가능한 추상적인 것에 의존하게 만들고, 가능하면 구체적인 것은 숨기자!
-> 캡슐화의 정의
- 클라이언트 객체가 구체적인 것을 알지 못하도록 숨기는 것을 캡슐화라고 한다!
* 계산기 LV4 의 전제는 각 Operator class 가 현재는 추상적인 객체이나 구체적인 것으로 변경될 수 있다고 가정,
예를 들어 + - * / 외 추가적인 기능이 생길 수가 있고, 즉 Calculator 가 변경의 전파 위험을 받을 수 있어 (Calculator 코드도 함께 수정이 필요) 변경이 필요하다.
이런 변경의 전파에서 벗어나기 위해 AbstractOperator 를 만들어 각 Operator class 내 동일한 추상적인 개념을 가져와,
Calculator가 AbstractOperator 에 의존하게 만드는 것, 그래서 각 Operator 의 구체적인 내용을 모르는 것이 핵심이다.
업캐스팅
추상 클래스, 인터페이스를 활용할 때는 업캐스팅의 개념을 알고 가는 것이 좋은데.
업캐스팅은 하위 클래스의 인스턴스를 상위 클래스 타입으로 변환하는 것을 말하며,
이는 항상 안전하게 수행할 수 있으며 별도의 검사나 예외 처리가 필요하지 않다.
아래 예시를 참고해보면,
open class Parent {
val name: String = "parent"
val age: Int = 58
}
class Child: Parent() {
val childAge: Int = 28
}
fun main() {
val child = Child()
val newParent = child as Parent // 업캐스팅
println(newParent.age) // 60
}
Child 클래스가 존재하고 이에 대한 부모 클래스로 Parent 클래스가 존재한다.
메인 함수에서는 Child를 인스턴스화하여 필드에 할당하고 Parent로 타입 변환을 시도. 그리고 Parent 클래스 내에 age를 호출했을 때 정상적으로 58이 나오는 것을 볼 수 있다.
이와 같이식 하위 클래스를 상위 클래스 타입으로 변환을 한 것을 업캐스팅이라고 하며 이는 다형성을 제공할 수 있다.
객체지향 구조를 잘 이해하고 과제나 프로젝트가 주어지면 객체를 잘 나누어 실행할 수 있을까? 가슴이 벅찬다.
본격적인 팀 프로젝트 진행 전 과제를 진행할 때 객체지향 프로그래밍을 잘 활용해보는 것을 수시로 연습해봐야겠다.
내가 처음 제출한 과제는 해당 이론을 전혀 이해못하고 진행했구나 싶어 다시 한 번 예외처리나 다양한 기능들을 추가하여 계산기를 구현해봐야겠다. 이번 주도 화이팅