Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 234 additions & 0 deletions 클린코드_4주차.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
SOLID원칙(객체지향 5대 원칙)

1. SRP 단일 책임 원칙 (Single Responsibility Principle)
- 단 하나의 책임(기능)만을 갖는다
- 버그 발생 가능성을 낮춘다

=> 좋은 설계 -> 분리된 클래스(함수).
=> 높은 응집도 + 낮은 결합도

연산을 하는 함수가 있다,,만약 알람기능을 추가하고 싶다면,,?
함수(Calculator)
+add()
+subtraction()
+multiply()
+division()
+alarm() (x)

또다른 함수(Alarm)을 만든다.



"나는 A이자, B이자, C입니다"
=> "나는 A" + "나는 B" + "나는 C"


2. OCP 개방 폐쇄 원칙 (Open-Closed Principle)
- 기존에선 닫혀있고, 추가/확장에서 열려있다

=> 변경에 초점을 맞추자
=> 자주 변하면,쉽게 변경할 수 있게 자주 변경되지 않으면 수정에 영향이 없게

ex) 더 많은 기능을 부여하고 싶다
-> 기존 코드를 바꿔서 기능을 부여한다(x)
-> 기존은 냅두고, 새로운 함수를 추가한다(O)

=> 캡슐화 - 인터페이스에 공통으로 쓰이는 기능을 정의한다

public class Client {
public static void main(String args[]){
Animal cat = new Cat();
Animal dog = new Dog();

cat.crying();
dog.crying();
}
}

어떤 동물이 추가되던, 인터페이스에 있는 crying() 기능을 쓸 수 있다. = 쉬운 확장이 가능하다.



3. LSP 리스코프 치환 원칙 (Liskov Substitution Principle)
=> 자식이 할 수 있으면 부모도 할 수 있어야 한다
=> 부모/자식간의 일관성 필요
=> 과연 올바른 상속 관계인가?

ex)

부모는 아이스크림을 먹을 수 있다

자식은 팽이팽이를 먹을 수 있다(O)
자식은 호떡을 먹을 수 있다(X)

open class Rectangle(open val width: Int, open val height: Int) {

open fun getArea() = width * height
}


class Square(
override val width: Int,
override val height: Int) : Rectangle(width, height) {

//super.getArea()를 사용하지 않고 정사각형에 맞게 로직을 수정했습니다.
//이렇게 하면 상위 타입(Rectangle)의 기능을 제대로 구현하지 못하게 됩니다.
override fun getArea(): Int {
//return super.getArea()
return width * width
}




abstract class Shape {

abstract fun getArea(): Int
}

class Rectangle(val width: Int, val height: Int): Shape() {

override fun getArea() = width * height
}


class Square(val width: Int) : Shape() {

override fun getArea() = width * width
}

class DoWork {

fun work() {

val rectangle: Shape = Rectangle(5,4)

if(isCheckedRectangle(rectangle)) {
println(rectangle.getArea())
} else {
throw RuntimeException()
}


val square: Shape = Square(5)

if(isCheckedShape(square)) {
println(square.getArea())
} else {
throw RuntimeException()
}
}

fun isCheckedRectangle(rectangle: Shape) = rectangle.getArea() == 20

fun isCheckedShape(square: Shape) = square.getArea() == 25


4. ISP 인터페이스 분리 원칙 (Interface Segregation Principle)
=> 사용하지 않는 인터페이스는 구현하지 않는다. 영향을 받아서도 안된다. 즉, 의존하면 안된다.
=> 낭비+버그 유발
=> 일반적 인터페이스(X) 여러개의 구체적 기능을 하는 개개별의 인터페이스(O)

interface Calculator {

fun add()

fun subtract()

fun multiply()

class Calculator2019: Calculator {

override fun add() {
println("2019 add()")
}

override fun subtract() {
println("2019 subtract()")
}

override fun multiply() {
println("2019 multiply()")
}


class Calculator2015: Calculator {

override fun add() {
println("2015 add()")
}

override fun subtract() {
println("2015 substract()")
}

override fun multiply() {
println("...")
}

interface Calculator {

fun add()

fun subtract()

}

interface CalculatorRecent {

fun multiply()
}

class Calculator2015: Calculator {

override fun add() {
println("2015 add()")
}

override fun subtract() {
println("2015 substract()")
}

}

class Calculator2019: Calculator, CalculatorRecent {

override fun add() {
println("2019 add()")
}

override fun subtract() {
println("2019 subtract()")
}

override fun multiply() {
println("2019 multiply()")
}



5. DIP 의존 역전 원칙 (Dependent Inversion Principle)
=> 변화하기 "어려운" 것에 의존한다
=> 변화하기 어려운것? -> 추상 클래스, 인터페이스

=> 변화에 유연한 설계가 가능해진다

추상 ->의존 구체(O)
구체 ->의존 추상(x)

고수준 모듈 (또는 클래스): 도구와 함께 동작하는 클래스.
저수준 모듈 (또는 클래스): 수행하기 위한 도구.
추상: 두 클래스를 연결하는 인터페이스
구체: 도구가 동작하는 방법

"클래스 + 도구" 보다 좋은 방법은 "클래스 + 인터페이스 + 도구"
클래스와 인터페이스는 도구가 어떻게 동작하는지 알 수 없어야 한다.

=> 인터페이스(추상)을 통하여 고수준 모듈이 저수준 모듈에 대한 의존성을 줄인다.

그림 :
https://medium.com/backticks-tildes/the-s-o-l-i-d-principles-in-pictures-b34ce2f1e898