본문 바로가기

TIL

[TIL] 객체 지향 프로그래밍 ( Objective Oriented Programming )

Topic =  객체 지향 프로그래밍에 대해서 학습

 

 


 

객체 지향 프로그래밍 (OOP)

 

OOP는 프로퍼티와 메서드를 갖는 데이터를 묶어서 정의하고, 그 정의된 내용을 객체라는 매개체를 생성하여 사용할 수 있게 한다. 그 객체들간 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍 방법으로, 여러 독립적인 부품들의 조합, 즉 객체들의 유기적인 협력과 결합을 통한 프로그래밍 방법이다.

 

자동차를 통해 간단한 예제를 만들어 보자.

 

프로퍼티?

class Car (val model: String, val engineDisplacement: Double){
    var isEngineRunning: Boolean = false
}
  • 각기 다른 자동차에 대한 정보를 정의할 수 있도록 모델명, 배기량은 Car class의 () 내부에 생성자 프로퍼티로 지정해준다
    • 모델과 배기량은 자동차 객체마다 다를 수 있는 자동차 각각의 특성이므로 객체 생성 시 할당된 프로퍼티에 따른 자동차 객체를 생성할 수 있는 것이다.
    • 객체의 () 내부에 존재하는 생성자 프로퍼티는 해당 Car 객체를 생성할 때 반드시 필요.
  • 멤버 변수
    • 멤버 변수는 isEngineRunning과 같이 객체의 {} 내부에서 정의된 프로퍼티로, 해당 객체의 모든 인스턴스에서 공유된다

 

  • ✔️ 그렇다면 fun testA( string: String ){} 와 같은 함수에서 string이 프로퍼티인가? 
    • → 정답은 아니다, 해당 내용에 대해서는 추가로 작성 예정

 

메서드?

  • Car라는 객체는 모델,배기량과 상관 없이 엔진을 켜고 끌 수 있다
class Car(val model: String, val engineDisplacement: Double) {
    var isEngineRunning: Boolean = false

    fun startEngine() {
        if (!isEngineRunning) {
            println("$model의 엔진이 시작됩니다.")
            isEngineRunning = true
        } else {
            println("$model의 엔진은 이미 가동 중입니다.")
        }
    }

    fun stopEngine() {
        if (isEngineRunning) {
            println("$model의 엔진이 정지됩니다.")
            isEngineRunning = false
        } else {
            println("$model의 엔진은 이미 꺼져 있습니다.")
        }
    }
}

 

 

 

 


 

 

객체지향의 5가지 원칙 - SOLID

 

 

SRP - 단일 책임 원칙 ( Single Responsibility Principle)

핵심 - 하나의 클래스는 하나의 책임을 가져야 한다 ( 변경 가능성 )

  • 예제를 통해 이해해보자, 게시글에 지도와 마커를 통해 게시글에서 알리고자 하는 위치정보를 게시글 사용자가 확인할 수 있도록 해주어야 한다.  게시글 작성자는 지도객체에서 Marker의 위치를 변경할 수 있고 수정된 내용을 적용할 수 있는 버튼이 필요하고, 게시글 작성자가 아닌 일반 사용자는 지도의 Marker위치를 이동할 수 없으며 별다른 수정 사항이 존재하지 않으니 적용  버튼 또한 필요하지 않는 상황이다.

 

  • 위와 같은 상황에서 하나의 MapViewFragment로 관리할 경우 생기는 문제
    • 지도 클릭 이벤트로 게시글 작성자의 경우 마커를 생성하며, 작성자 외의 사용자들은 별도의 이벤트가 없어야 하는데, 이를 위해서는 fragment 내부에서 현재 이용하는 사용자가 작성자인지, 일반 사용자인지를 구분해야 하는 로직이 추가 되어야 한다.
    • 만약 기존에 동일한 마커를 사용하다가,  게시글 작성자 화면 내부의 지도 마커 옵션에 대한 변경이 필요할 경우, 마커 옵션을 변경하게 되면 작성자가 아닌 사용자에게서도 변경된 마커 옵션이 적용되어 진다. 때문에 기능을 추가할 뿐만 아니라 별도로 각각 다른 옵션을 구분해주어야 하는 문제가 발생한다.
    • 하나의 클래스 안에서 마커 옵션을 각각 구분해주어야 하고 이는 추후에 마커 관련 옵션의 수정이 필요할 경우 유지보수가 어려워지는 결과를 낳을 수 있다.

 

  • 그렇다면 어떤 방식으로 SRP 원칙을 충족시킬 수 있을까? - ⭐ 변경 가능성에 주목
    • 게시글 작성자의 경우 MapViewFragment를 상속받는 서브 클래스인 AuthorMapViewFragment를 생성하여 게시글 작성자가 상호작용할 수 있는 옵션으로 지도객체를 활용할 수 있도록 해주고, 일반 사용자의 경우도 별도로 MapViewFragment의 서브 클래스를 생성하여 일반 사용자에 해당하는 지도 옵션을 지정해준다. 
    • 만약, MapViewFragment에서 게시글 작성자가 구분되어서 다르게 적용되어야 하는 부분이 마커 옵션 뿐이라면, MapViewFragment를 상속받는 작성자, 일반 사용자로 나누어 서브 클래스를 갖는 것보다 하나의 MapViewFragment 에서 게시글 작성자와 일반 사용자로 구분하여 마커 옵션을 적용하기 위한 마커 옵션 Interface or Class를 활용하는 방법이 적합할 수 있다.

 

 

OCP - 개방 폐쇄의 원칙 ( Open Closed Principle )

핵심 - 새로운 기능을 추가할 때, 구현부가 수정되지 않아야 한다

  • 개방 폐쇄 원칙이란, 소프트웨어 요소 ( 클래스, 모듈, 함수 ) 는 확장에는 개방적이고, 변경에 대해서는 폐쇄적이어야 한다는 원칙이다.
    • 여기서 확장과 변경에 대해서 조금 더 풀어서 설명해보면, 확장은 상품을 판매하는 입장을 예시로 든다면 '상품' 이라는 틀 안에 Snack과 Liquor라는 Sub클래스가 존재할 때 새로운 Product인 IceCream이 추가되는 상황과 같이 새로운 기능의 확장을 얘기한다.
    • 변경은 새로운 기능이 추가 등으로 인한 확장이 되었을 때 별도로 변경해주어야 하는 것을 의미한다. 아래의 예시를 통해서 확인해보자.
    • OCP를 통해서 얻을 수 있는 이점
      • 유지보수성, 재사용성, 일관성 향상
        • 기존 코드를 수정하지 않고 새로운 기능을 추가하거나 기존 기능을 변경할 수 있어 유지보수성을 향상 시킬 수 있다.
      • 확장성 향상
        • 새로운 기능을 추가할 때 기존 구현부의 코드를 수정하지 않고도 확장할 수 있다.

 

- 개방 폐쇄의 원칙을 지키지 않은 예시 - 

 

 1 . IceCream이 추가되기 전 Snack과 Liquor을 판매하는 상황 코드

class Product(category : String){
    var category = category
}

class ProductDisplay(){
    fun display(product: Product){
        if (product.category=="Liquor") {
            println("주류 코너에 술을 진열 합니다.")
        } else if (product.category=="Snack"){
            println("스낵 코너에 과자를 진열 합니다.")
        }
    }
}

fun main() {
    val productDisplay = ProductDisplay()
    val snack  = Product("Snack")
    val liquor  = Product("Liquor")

    productDisplay.display(snack)
    productDisplay.display(liquor)

}

 

 

2. 아이스크림 상품이 추가가 된 코드

class Product(category : String){
    var category = category
}

class ProductDisplay(){
    fun display(product: Product){
        if (product.category=="Liquor") {
            println("주류 코너에 술을 진열 합니다.")
        } else if (product.category=="Snack"){
            println("스낵 코너에 과자를 진열 합니다.")
        } else if (product.category == "IceCream"){
            println(" 아이스크림 코너에 아이스크림을 진열합니다")
        }
    }
}

fun main() {
    val productDisplay = ProductDisplay()
    val snack  = Product("Snack")
    val liquor  = Product("Liquor")
    val iceCream = Product("IceCream")

    productDisplay.display(snack)
    productDisplay.display(liquor)
    productDisPlay.display(iceCream)

}

 

위와 같이 상품이 추가될 때 마다 ProductDisplay에서 새롭게 추가된 상품의 display를 별도로 추가해주어야 하는 문제가 발생한다.

 

 

 

- 개방 폐쇄 원칙을 적용한 예시 -

// 개방 폐쇄 원칙을 적용한 예시
interface Product{
    fun display(){
        println("상품을 진열 합니다.")
    }
}

class Snack():Product{
    override fun display() {
        println("스낵 코너에 과자를 진열 합니다")
    }
}

class Liquor():Product{
    override fun display() {
        println("주류 코너에 술을 진열 합니다")
    }
}

class IceCream():Product{
    override fun display() {
        println("아이스크림 코너에 아이스크림을 진열 합니다.")
    }
}

class ProductDisplay(){
    fun display(product: Product){
        product.display()
    }
}

fun main() {
    val productDisplay = ProductDisplay()
    
    val snack : Product = Snack()
    val liquor : Product = Liquor()
    val iceCream : Product = IceCream()

    productDisplay.display(snack)
    productDisplay.display(liquor)
    productDisplay.display(iceCream)
    
}
  • Product에 유제품, 생필품 등등의 새로운 카테고리가 추가되어도 객체만 추가될 뿐,  ProductDisplay와 구현부 코드에는 변경이 없다.

 

 

예시를 통해 확인할 수 있었던 것과 같이 OOP의 핵심 요소 추상화, 다형성, 상속을 통해 OCP 원칙을 충족시킬 수 있었다. 상속과 다형성으로 인해 확장성은 Open 되도록 하고, 추상화를 통해 변경을 Closed 한다.

 

 

 

 

LSP - 리스코프 치환 원칙 ( Liscov Substitutuon Principle )

핵심 - 서브 클래스는 상위 클래스의 역할, 기능에서 벗어나지 않아야 한다.

핵심 한 문단을 통해서 개념을 정리하는 것이 어려운 것 같다. 간단한 예제를 들어보면 마트에서 각각의 코너에 필요한 물건들을 진열해야 하는데, 코너 중 주류코너가 있다고 가정해보자, 주류라는 interface의 메서드에는 주류 코너에 물건을 진열하는 메서드가 있고 주류 class 하위 클래스에는 맥주, 소주, 보드카, 막걸리 등이 있을 때 소주, 맥주 등의 분류가 주류라고 간단하게 분류되어 주류 코너로 간다고 해도 특별한 문제가 없을 것이다.

패키징 되어져 있는 와인 상품의 경우 기본적인 주류 인터페이스의 주류 코너에 진열하는 메서드를 통해 주류 코너에 물건을 진열할 수 없어 주류 인터페이스의 메서드를 오버라이딩 해서 별도로 로직을 작성하게 된다면, 이는 상위클래스의 명세를 벗어난 것으로 추상화한 타입을 그 하위 타입의 객체로 변환했을 때 예상한대로 동작하지 않는 것을 방지할 수 없게 되어 버린다.

이러한 이유로 패키징된 와인과 같이 상위 클래스인 주류와 별도로 구분하여 작성할 수 있도록 하여야 한다.

 

설명 이해를 위해 사진 추가

 

 

 

 

ISP - 인터페이스 분리 원칙 ( Interface Segregation Principle )

 

 

ISP의 핵심 - 인터페이스는 클라이언트( 인터페이스를 구현하는 부분 )가 필요로 하는 메서드만 사용할 수 있도록 하여 단일 책임 원칙을 통해 인터페이스의 의존성을 낮추고 유연성 및 확장 가능성을 향상시킨다.

 

a,b,c 메서드를 가지고 있는 A 인터페이스를 구현하는 (가) 클라이언트 ( 인터페이스를 사용하는 부분 )는 A 인터페이스에서 사용하지 않는 기능에 대한 처리를 하지 않도록 해야한다는 것이다.

 

인터페이스를 책임, 변경 가능성에 따라 분리하여 각 인터페이스의 재사용성을 늘리고 해당 인터페이스를 구현하는 ( RepositoryImpl 등... ) 클래스의 관리가 쉬워진다. ISP원칙은 설계 시 인터페이스를 사용하는 구현부가 어떻게 확장될 수 있는지에 대해서 생각해보아야 할 것 같다. 

  • 만약 게시글 카테고리가 모든 사용자가 사용할 수 있는 게시판 하나인 앱의 경우 게시글 인터페이스를 생성하게 될 경우 아래와 같이 작성할 수 있다.
interface post{
    fun create()
    fun read()
    fun update()
    fun delete()
}
  • 하지만 공지사항 게시판을 추가하여 일반 사용자는 공지사항 게시판에서 글을 읽기만 가능하도록 기능을 추가하려는데 post를 그대로 사용할 경우 필요로 하지 않는 read, update, delete 의 경우를 별도로 처리해주어야 하는 상황이 생긴다. 때문에 공지사항 게시판을 추가할 경우 아래와 같이 notification 클래스를 별도로 구분하여 사용할 수 있다.

 

interface post{
    fun create()
    fun read()
    fun update()
    fun delete()
}

interface notification{
    fun read()
}

 

 

 

DIP - 의존성 역전 원칙 ( Dependency Inversion Principle)

 

핵심 - 구체화된 것에 의존하지 말고, 추상화에 의존해야 한다.

 

Car, Bus, Bike class안에 각각의 drive()함수를 정의해두고, Human Class에서 Car class 객체를 생성한 뒤 drive 메서드를 실행한다고 해보자. Car 클래와 Bus, Bike 각각의 클래스는 모듈의 구체적인 구현을 담당하는 저수준의 모듈이다. DIP는 이러한 저수준의 모듈을 통해 Human 클래스가 직접적으로 Car, Bus 객체에 의존하지 않도록 Vehicle라는 고수준 모듈을 Human이 의존함으로 이동수단 각각의 객체들 사이에 의존성을 낮출 수 있다.

 

Human 클래스가 추상화된 인터페이스 Vehicle 클래스를 통해 접근함으로 Car, Bus, Bike 각각의 객체들 사이의 의존성을 낮추는 것 뿐 아니라 코드의 재사용성과 유연성 또한 높일 수 있으며 객체가 서로를 참조하는 순환 참조 문제를 해결할 수 있다.

 

⭐DIP 핵심 - 구체화 된 것에 의존하지 말고 추상화에 의존해야한다.
구체화 된 것에 의존할 경우 순환 종속성의 문제가 생길 수 있는데. 순환 종속성이 있는 원형 참조 코드는 객체의 의존성 구조가 복잡해지면서 코드의 이해가 어려워지며, 새로운 기능을 추가하거나 수정할 때 예기치 않은 문제가 발생하기 쉽게 되는 등의 유지보수성에도 문제가 발생한다. 이와 같이 순환 종속성의 문제는 선능적인 문제와 더불어 단위 테스트 및 통합 테스트가 복잡해지는 등 피해야 할 문제이고 이러한 문제는 의존성 역전 원칙을 통해 해결할 수 있다.

 

 

 

문제 상황 - 두개 이상의 클래스, 모듈이 직접 또는 간접적으로 서로에게 의존하는 상황인 순환 종속성 ( = 순환 참조 )예제

// 고수준 모듈
class OrderProcessor {
    private val paymentProcessor = PaymentProcessor()

    fun processOrder(order: Order) {
        // 주문 처리 로직
        paymentProcessor.processPayment(order)
    }
}

// 저수준 모듈
class PaymentProcessor {
    private val orderProcessor = OrderProcessor()

    fun processPayment(order: Order) {
        // 결제 처리 로직
        orderProcessor.processOrder(order)
    }
}

class Order {
    // 주문 상세 정보
}

fun main() {
    val orderProcessor = OrderProcessor()
    orderProcessor.processOrder(Order())
}

 

 

의존성 역전 원칙 적용을 통해 순환 참조 문제를 해결하는 로직

// 추상화
interface PaymentProcessor {
    fun processPayment(order: Order)
}

// 고수준 모듈
class OrderProcessor(private val paymentProcessor: PaymentProcessor) {
    fun processOrder(order: Order) {
        // 주문 처리 로직
        paymentProcessor.processPayment(order)
    }
}

// 저수준 모듈
class CreditCardPaymentProcessor : PaymentProcessor {
    override fun processPayment(order: Order) {
        // 신용카드 결제를 위한 처리 로직
    }
}

class Order {
    // 주문 상세 정보
}

fun main() {
    val creditCardPaymentProcessor = CreditCardPaymentProcessor()
    val orderProcessor = OrderProcessor(creditCardPaymentProcessor)
    orderProcessor.processOrder(Order())
}

 

 

➕ 의존성 역전 원칙 ( DIP ) 를 충족시키기 위한 방법인 의존성 주입에 관한 게시글 추가.

 

[TIL] Kotlin 의존성 주입 ( Dependency Injection ) 개념편

Topic = 의존성 주입의 개념에 대해서 학습해보자. DI 의존성 주입 ( Dependency Injection ) 의존성 주입이란?? OOP 원칙 중 DIP 원칙을 지키기 위해 사용되는 방법으로, 하나의 객체가 다른 객체의 의존성

junes-daily.tistory.com

 


 

 

객체 지향 프로그래밍(SOLID)을 위한 4 요소

 

 

위에서 SOLID를 통해 확인한 것과 같이 객체 지향 프로그래밍, OOP의 핵심은 아래와 같이 정리할 수 있다.
1. 프로그램을 보다 유연하고 변경이 용이하도록 만드는 것
2. 각각의 역할을 분리하여 독립적인 역할을 수행할 수 있도록 하는 것.
3. 코드의 재사용을 통해 반복적인 코드를 최소화.

 

SOLID 원칙을 적용하여 객체 지향적 설계를 위한 요소로는 여러가지 요소가 있을 수 있지만, 그중 대표적인 요소로는 추상화, 상속, 다형성, 캡슐화를 뽑을 수 있다.

 

추상화 ( Abstraction )

 

→ 불필요한 세부 사항들은 제거하고 본질적이고 공통적인 부분만을 추출하여 표현하는 것
예시 ) 자동차와 오토바이 - 자가용

추상화 방법 → abstract class, interface 를 사용, Interface는 특정 객체의 역할만을 정의하고 실제 구현은 각각의 객체에서 구현하므로 
☆역할과 구현의 분리

 

 

 

상속 ( Inheritance )

 

→ 클래스 간 공유될 수 있는 속성과 기능들을 상위 클래스로 추상화 시켜 상위클래스로부터 확장된 여러 개의 하위 클래스들이 상위 클래스의 속성과 기능들을 간편하게 사용할 수 있도록 하는 것.
→ 한 번의 정의로 재사용을 통해 반복적인 코드를 최소화 하고, 공유하는 속성과 기능에 간편하게 접근.
→ 상속을 받는 하위 클래스는 상위 클래스의 메서드를 오버라이딩 하여 재정의하여서 사용할 수도 있다.

☆ 상속과 인터페이스의 차이 → 상속의 경우 상위 클래스의 속성과 기능들을 하위 클래스에서 그대로 받아 사용하거나, 오버라이딩을 통해 선택적으로 재정의할 수 있다, 인터페이스의 경우 정의된 추상 메서드의 내용이 하위 클래스에서 반드시 정의 되어야 한다.


☆ 추가 내용 : 상속 관계의 경우 인터페이스를 사용하는 구현에 비해 추상화의 정도가 낮다, 인터페이스가 역할에 해당하는 껍데기만 정의해두고, 하위 클래스에서 구체적인 구현을 하도록 강제하는 데에 비해, 상속 관계의 경우 상황에 따라 모든 구체적인 내용들을 정의해두고 하위 클래스에서는 그것을 단순히 가져다가 재사용할 수 있는 것.

 

 

 

다형성 ( Polymorphism ) → OOP의 핵심 

 

객체의 속성이나 기능이 상황에 따라 여러 가지의 형태를 가질 수 있는 것. A가 이용하는 대중교통은 마을 버스이고 B가 이용하는 대중교통은 지하철, C가 이용하는 대중교통은 광역버스이다. 이처럼 대중교통이라는 큰 틀의 상위 클래스 하위에 마을버스, 광역버스, 지하철 등으로 나뉘어서 상황에 따라 대중교통이라는 틀 안에서 버스, 전철 등의 역할을 수행할 수 있는 특성을 다형성이라고 한다.

 

OOP에서의 다형성의 특성으로 한 타입의 참조변수를 통해 여러 타입의 객체를 참조할 수 있다.이는 대중교통 이라는 참조변수를 사용하여, Bus, Subway 등의 교통수단을 참조할 수 있다.

 

설명을 위해 예시를 넣어두었는데 아래에서 추상화, 상속 방식에서 추가로 작성.

    val bus2 : Transport = Bus()
    bus2.goToWork()

 

 

다형성 예제 Case 1. 출퇴근 시 대중교통을 이용하는 상황에서의 대중교통 객체

 

잘못된 접근 방법

  • 다형성의 요소가 적용이 되지 않았다 → 지하철, 시내 버스, 광역 버스 각각에 중복된 메서드가 포함된다

 

올바른 접근 방법

  • 하나의 메서드를 오버라이드, 오버로딩하여 사용함으로써 동일한 출퇴근 메서드 코드의 중복을 줄일 수 있다

 

추상화, 상속, 다형성 예제

 

  • 다양한 방식으로 추상화, 상속, 다형성의 적용 방식 예제이다
fun main(){

    val bus = Bus()
    bus.goToWork()

    val subway = Subway("전철")
    subway.goToWork()

    val cityBus = CityBus()
    cityBus.goToWork()

    val commute = Commute2()
    commute.commute(cityBus)
    
    // 업 캐스팅 ( UpCasting ) 
    val bus2 : Transport = Bus()
    bus2.goToWork()
}


interface Transport{
    var type : String
    fun goToWork(){
        println("$type 에 탑승합니다")
        println("$type 이 이동합니다.")
        println("$type 이 도착했습니다")
    }
}

// Transport의 goToWork 메서드 오버라이딩
class Bus(override var type: String= "버스"):Transport{
    override fun goToWork() {
        println("$type 에 탑승했습니다")
        println("$type 가 이동합니다")
        println("$type 가 도착했습니다")
    }
}

class Subway(override var type: String):Transport{
}

class CityBus(override var type: String= "시내버스"):Transport{
}

class Commute(){
    fun commute(transport: Transport){
        transport.goToWork()
    }
}

 

 

 

 

 

캡슐화

 

 

캡슐화란 데이터와 데이터를 조작하는 함수를 하나의 클래스 단위로 묶는 것과 같이 클래스 안에 서로 연관있는 속성과 기능을 하나의 캡슐로 만들어 데이터를 외부로부터 보호하는 것이다. 

 

캡슐화는 접근제한자를 사용하여 클래스 필드나 메서드는 외부에서 직접적으로 접근할 수 없도록 하는 것인데 간단한 상황을 예로 들어보자. 물건의 가격표가 붙어 있는 상황에서 가격표에 별도로 코팅이 되어있지 않아 해당 가격표에 직접적으로 접근하여 변경된 가격을 위에 작성하는 등의 접근이 아래와 같이 가능하다.

class Product(){
   var price : Int = 0

}

class Display(){
    val product = Product()
    
    product.price = 100
}
  • 사과의 가격이 1000원으로 작성되어야 하는데, 실수로 100원으로 작성되어졌다.
  • 위와 같은 문제를 해결하기 위해 Product 클래스 내부의 메서드를 통해서 접근할 수 있도록 할 수 있다.

 

internal class Product() {
    private var price: Int = 0

    fun setPrice(newPrice: Int) {
        if (newPrice > 700 && newPrice < 1500) {
            price = newPrice
        } else {

        }
    }

    fun getPrice(): Int {
        return price
    }
}

fun main() {
    val product = Product()

    product.setPrice(1000)
    println(product.getPrice()) // 1000
}
  • Product 클래스 내부에서 유효성 검사를 통해 유효한 값이 할당될 경우에만 값이 변경될 수 있도록 수정되었고, internal 접근 제한자를 통해 모듈 내, 즉 예시의 마트 직원들에게만 접근을 통해 수정할 수 있도록 해서 데이터의 외부 접근으로 인한 파싱을 제한하여 데이터의 무결성을 보장할 수 있고 코드의 유지보수성 및 안정성을 향상 시킬 수 있다.

 

 

 

 

 

접근 제한자

 

Public 객체 생성 시 Default 값으로 어디에서나 접근할 수 있다
Private 동일한 클래스 내부에서만 접근할 수 있다
Internal 같은 모듈 내에서만 접근할 수 있다
Protected 기본적으로 Private이지만 상속을 받은 경우에 타 모듈에서도 접근할 수 있

 

// getter,setter의 가시성은 기본적으로 프로퍼티의 가시성과 같다.
// 하지만 원한다면 setter method는 가시성 접근 제어자로 가시성을 변경할 수 있다
class LenghtCounter{
    var counter:Int =0
        private set
    fun addWord(word: String){
        counter += word.length
    }
}

 

 

 

 

 


 

[ A. 오늘 복습한 내용 / B. 다음에 학습할 내용 ]

A. 코드 리팩토링 시 어떤식으로 코드를 작성해야할지 방향이 조금 잡힌 것 같다.

 

B. 함수형 프로그래밍, 파라미터 가변인자 등

 

B. ViewModel Interface, abstract class 차이점 ( 프로그래밍 패러다임, 디자인 패턴 측면 ) 자세하게 학습하기

 


 

[오류,에러 등등]

1. 이론 위주 학습으로 특별한 오류는 없었다.

 

 

 


 

[느낀 점]

1. 주변에서 좋은 영향을 주는 분들 덕분에 배우는 것도 많고, 재미있고 능률도 많이 올라가는 것 같아서 나도 좋은 영향을 많이 주고 싶다.

 

 

 


[Reference]

 

// OOP

https://velog.io/@jxxn_a/Kotlin-TIL2

 

Kotlin TIL(2) [객체지향 프로그래밍, 예외처리, 지연초기화, 컬렉션, 싱글턴]

프로그래밍에서 필요한 데이터를 추상화 시켜 상태와 행위를 가진 객체로 만들고, 객체들간의 상호작용을 통해 로직을 구성하는 프로그래밍 방법이다.◼ 접근제한자 객체를 이용해서 변수나

velog.io

https://www.codestates.com/blog/content/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%8A%B9%EC%A7%95

 

객체 지향 프로그래밍의 4가지 특징ㅣ추상화, 상속, 다형성, 캡슐화 -

객체 지향 프로그래밍은 객체의 유기적인 협력과 결합으로 파악하고자 하는 컴퓨터 프로그래밍의 패러다임을 의미합니다. 객체 지향 프로그래밍의 기본적인 개념과 그 설계를 바르게 하기 위

www.codestates.com