[TIL] 객체 지향 프로그래밍 <클래스와 객체> (1/2)

23026 단어 TIL_KotlinTILTIL

Do it! 코틀린 프로그래밍 [둘째마당, 객체 지향 프로그래밍] 학습

객체 지향 프로그래밍 (OOP: Object-Oriented Programming) 은 프로그램의 구조를 객체 간 상호작용으로서 표현하는 프로그래밍 방식으로, 절차적 프로그래밍의 한계를 극복하고자 나온 방법론

📍 절차적 프로그래밍? 코딩한 순서대로 프로그램이 수행될 수 있도록 작성하는 방법론. 단순하고 오류를 예측하기 쉽지만 구조적이지 못해 복잡한 프로그램을 설계하기 어려움

✏️1. 클래스와 객체의 정의✏️

1-1. 객체지향 프로그래밍과 용어

📌객체 지향 프로그래밍의 용어 정리

코틀린다른 언어
클래스(Class)분류, 범주
프로퍼티(Property)속성(Attribute), 변수(Variable), 필드(Field), 데이터(Data)
메서드(Method)함수(Function), 동작(Operation), 행동(Behavior)
객체(Object)인스턴스(Instance)

📌 클래스의 멤버가 될 수 있는 것

  • 생성자와 초기화 블록 : 객체가 생성될 때 자동 실행되는 메서드 또는 코드 블록
  • 프로퍼티 : 변수의 이름과 변수의 접근 함수가 포함된 형태
  • 메서드 : 일반적인 함수의 형태
  • 중첩(Nested)클래스와 이너(Inner)클래스:클래스 내부에 구성되는 클래스
  • 객체 선언:클래스 없이 접근할 수 있는 객체


1-2. 클래스

📌 클래스 다이어그램

  • 통합 모델링 언어(UML: Unified Modeling Language) : 객체 지향 프로그램 설계를 위한 다이어그램 표기법. 개발 단계에서 팀 의사소통을 우너활히 하는데 도움
  • 다이어그램 기법의 종류
    • 클래스(Class) 다이어그램
      • 클래스의 정의와 관계를 나타내는 다이어그램
      • 클래스 이름, 프로퍼티, 메서드를 손쉽게 파악 할 수 있음
    • 시퀀스(Sequence) 다이어그램
      • 시간의 개념을 통해 클래스에서 생성된 객체의 실행 흐름을 나타냄
      • 객체 간의 실행 순서를 보기에 적합
    • 유스 케이스(Use Case) 다이어그램
      • 사용자 관점에서 사용 방법에 대해 설명
      • 사용자가 어떤 식으로 사용할 것인지 보여줌
    • 상태 머신(State-Machine) 다이어그램
      - 시스템 관점에서 상태가 어떻게 변화하는지 나타냄
      - 객체의 상태변화를 볼 수 있음


📌 추상화 (Abstraction)

  • 목표로 하는 대상에 대해 필요한 만큼 속성과 동작을 정의하는 과정을 추성화라고 함

📌 클래스 선언하기

⬇️ 클래스 구조

class bird{} // 내용이 비어 있는 클래스 선언
class bird // 중괄호 생략 가능

class bird{
	// 프로퍼티
    // 메서드
}

⬇️ 클래스 선언과 객체 생성

class Bird {
	var name: String = "bird"
    
    fun fly() = println("bird $name")
}

fun main() {
	val coco = Bird() // 클래스 생성자를 통한 객체의 생성
    println("$coco.name") // 객체의 멤버 프로퍼티 읽기
    
    coco.fly() // 객체의 멤버 메서드 사용
}

📌 객체와 인스턴스 정리하기

  • 클래스로부터 객체(Object)를 생성해야만 클래스라는 개념의 실체인 객체가 물리적인 메모리 영역에서 실행됨. 이를 객체 지향 언어에서는 구체화 또는 인스턴스화(Instantiate)되었다고 함.
  • 메모리에 올라간 객체를 인스턴스(Instance)라고 함

✏️2. 생성자 (Constructor)✏️

클래스를 통해 객체가 만들어질 때 기본적으로 호출되는 함수를 생성자 라고함

⬇️ 생성자 선언

class 클래스 이름 constructor(필요한 매개변수..) { // 주 생성자 위치
	...
	constructor(필요한 매개변수..) { // 부 생성자 위치
    	// 프로퍼티(변수) 초기화
    }
}

2-1. 부 생성자

  • 클래스의 본문에 함수처럼 선언
class Bird {
	var name: String // 프로퍼티(변수)
    var wing: Int
    var beak: String
    
    // 부 생성자 - 매개변수를 통해 초기화할 프로퍼티에 지정
    constructor(name: String, wing: Int, beak: String) {
    	this.name = name // this.name 은 현재 클래스의 프로퍼티를 나타냄
        this.wing = wing
        this.beak = beak
    }
}

⬇️ this 키워드를 사용하지 않는 방법

class Bird {
	var name: String // 프로퍼티(변수)
    var wing: Int
    var beak: String
    
    constructor(_name: String, _wing: Int, _beak: String) {
    	name = _name // name : 클래스의 프로퍼티, _name : 생성자의 매개변수
        wing = _wing
        beak = _beak
    }
}

-> 프로퍼티와 매개변수를 구분하기 위해 다른 이름을 사용
-> 매개변수 이름 앞에 언더스코어(_) 사용

⬇️ 매개변수가 다르게 부 생성자를 여러개 선언

class Bird {  

	constructor(_name: String, _wing: Int, _beak: String) {
    	name = _name // name : 클래스의 프로퍼티, _name : 생성자의 매개변수
        wing = _wing
        beak = _beak
    }
    
    // 매개변수가 다른 또 다른 부 생성자
    constructor(_name: String, _wing: Int){
    	name = _name
        wing = _wing
        beak = "short"
    }
}

fun main() {
	val coco = Bird("coco", 2, "short")
    val noBeak = Bird("coco", 2)
}

2-2. 주 생성자

  • 클래스 이름과 함께 생성자 정의를 이용할 수 있는 기법
  • 클래스 이름과 블록 시작 부분 사이에 선언
// 주 생성자 선언
class Bird constructor(_name: String, _wing: Int, _beak: String) {
	var name: String // 프로퍼티(변수)
    var wing: Int
    var beak: String
}

// constructor 키워드 생략 가능
class Bird(_name: String, _wing: Int, _beak: String) {
	var name: String // 프로퍼티(변수)
    var wing: Int
    var beak: String
}

// 내부의 프로퍼티를 생략하고 생성자의 매개변수에 프로퍼티 선언
class Bird(val name: String, val wing: Int, val beak: String) { }

📌 초기화 블록을 가진 주 생성자

  • 초기화에 꼭 사용해야 할 코드가 있다면 init{...} 초기화 블록을 클래스 선언부에 넣어주어야 함
// 주 생성자 선언
class Bird(var name: String, var wing: Int, var beak: String) { 
	// 초기화 블록
	init{
    	println("---초기화 블록 시작---") // (1)
    }
}

fun main() {
	val coco = Bird("coco", 2, "short")
    // 객체 생성과 함꼐 초기화 블록이 수행
    // (1) 문구가 출력
}

📌 프로퍼티의 기본값 지정

// 프로퍼티의 기본값 지정 (name, wing의 기본값을 지정)
class Bird(var name: String = "coco", var wing: Int = 2, var beak: String) { }

fun main() {
	// 기본값이 있는 것은 생략, 없는것만 전달 가능
	val coco = Bird(beak = "short")
}

✏️3. 상속(Inheritance)과 다형성(Polymorphism)✏️

3-1. 상속(Inheritance)

자식 클래스를 만들 때 상위 클래스(부모 클래스)의 속성과 기능을 물려받아 계승하는 것

⬇️하위 클래스 선언하기

open class BaseClass(someArgs: Int) // 상속 가능한 클래스
class SomeClass(someArgs: Int) : BaseClass(someArgs) // 클래스 상속의 선언
class SomeClass : BaseClass { ..constructor().. }
  • 코틀린은 기본적으로 상속이 불가능하도록 선언됨. 클래스를 선언할 때 open키워드를 사용해야 파생 클래스에서 상속 가능
    • 📍 코틀린은 기본적으로 상속이 불가능하도록 선언됨. 하지만 자바는 기본적으로 선언하는 클래스가 상속 가능한 클래스이며, 자바에서 상속할 수 없는 클래스로 선언하려면 final 키워드를 사용해야함
  • 모든 클래스는 묵시적으로 Any를 최상위 클래스로 가짐

3-2. 다형성(Polymorphism)

이름이 동일하지만 매개변수가 서로 다른 형태를 취하거나 실행 결과를 다르게 가질 수 있는 것

📌 오버로딩(Overloading)

  • 동일한 클래스 안에서 같은 이름의 메서드가 매개변수만 달리해서 여러 번 정의될 수 있는 개념
  • 반환값은 동일하거나 달라질 수 있으나, 구현되는 동작은 대부분 동일
fun add(x: Int, y: Int): Int {
	return x + y
}

// 자료형이 다름
fun add(x: Double, y: Double): Double { 
	return x + y
}

// 매개변수의 개수가 다름
fun add(x: Int, y: Int, z: Int): Int {
	return x + y + z
}

📌 오버라이딩(Overriding)

  • 하위 클래스에서 새로 만들어지는 메서드가 이름이나 매개변수, 반환값이 이전 메서드와 똑같지만 새로 작성되는 것
  • 파생 클래스가 오버라이딩하기 위해 기반 클래스에서는 open 키워드, 파생 클래스에서는 override키워드를 각각 사용
// 부모 클래스
open class Bird{ // 상속 가능
	fun fly() { ... } // 최종 메서드로 오버라이딩 불가
    open fun sing() { ... } // 오버라이딩 가능
}

// 자식 클래스
class Lark() : Bird() {
	fun fly() { /* 재정의 */ } // !오류! fly()는 open키워드 X
    override fun sing() { /* 재정의 */ } // 오버라이딩 가능
}

좋은 웹페이지 즐겨찾기