Kotlin | SOLID 원칙

Colde
7 min readMay 20, 2022

--

SOLID 패턴 일종에 객체지향 프로그래밍을 하다보면 많이 다루는 패턴 중 하나이며, 그리고 또한 가장 기초적인 패턴 이자 가장 중요한 패턴 중 하나이다.

단순하게 코딩을 하다 보면 상당히 유지보수하는데 있어서 상당한 어려움을 격는다, 또한 확장성 또한 떨어지게 된다.

우리가 앞으로 10년이상 프로그래밍을 하는 사람 혹은 이 객체지향의 패러다임이 없어질때면 SOLID 패턴 또한 안익혀도 상관은 없다,

그리고 나는 객체지향적인 프로그램을 사용하지 않는다고 생각하면 단순하게 생각을 해도 좋다, 봐도되고 안봐도 된다.

Single Responsibility Principle (단일 책임 원칙)

단일 책임 원칙이란 “하나의 객체가 하나의 책임만 가지는 것이다” 라는 개념이다, 일단 이 문장이 약간 어렵다.

bad code

위의 코드는 나쁜 예제이다. 일단 Book 이라는 클래스에서 SaveBook 메소드는 두 개의 책임을 가진다. 일단 Book 이라는 객체는 title, author, descrition, pages 는 필드를 가지게 되고 saveBook 의 경우 데이터의 저장을 뜻하는 로직이기 때문에 필드 관리 와 기능적 메소드 를 분리를 해주어야 한다.

good code

Book Class , Persistence Class 두가지로 구성을 하고 Book 의 경우 필드를 책임지고 Persistence 의 경우 saveBook 기능적으로 책임을 지는 각각의 하나의 책임만 가지게 한다.

Open Close Principle (개방 폐쇄 원칙)

개방 폐쇄 원칙은 “확장에는 열어 있지만 수정에는 닫혀있는 원칙” 이라는 개념으로 기능이 변화하거나 확장되는 것은 되지만 말이 조금 어렵지만 한마디로 부분적인 수정은 되지만 계속 여러 군대에서 코드 수정을 하는 것은 객체 설계를 하는데 있어서 잘못된 설계라고 할 수 있다.

bad code

AreaCalculator Class의 calculatorRectangleArea , calculatorCircle 의 경우 RectangleArea , Circle Class 에 대한 기능을 각각 정의를 해주었다. 이것을 공통의 기능을 정의하고 (interface) 그리고 객체 안에 정의된 기능을 넣어주는 방법으로 객체를 정의 해서 하면 된다.

good code

Shape Interface 의 경우 calculatorArea 메소드를 통해서 기능을 정의한 뒤 Rectangle, Circle Class 에서 Shape 를 상속 받아서 메소드를 오버라이드 시켜서 정의를 해준다. 이때 Bad Code 에서 각각 로직을 정의 해주는 것을 객체에 넣어서 정의를 해준다.

Liskov Substitution Principle (리스코프 치환 원칙)

리스코프 치환 원칙은 “부모 객체를 호출하는 동작에서 지식 객체가 부모 객체를 완전하게 대체 할 수 있다는 원칙” 의 개념인다. 객체지향의 가장 큰 특징인 상속이 존재를 하는데 이는 상속을 받으면 부모 객체 와 자식 객체가 된다.

객체를 상속을 지속적으로 확장을 하다 보면 무모하게 변질된 코드가 발생이 될때도 있고 그와 더불어 객체의 의미가 모호해진다.

bad code

Rectangle Class 를 정의를 해서 상속을 통해서 Square 를 정의를 해주었다. 리스코프 치환 원칙에 위배 된다. 직사각형을 정사각형의 상속하는 것 자체가 개념적으로 맞지 않는다.

둘다 같은 “도형” 이라는 개념에 의해서는 두가지 종류가 된다. 이 코드를 변경을 하려면 Shape 이라는 클래스에서 정의를 해주는 것이 좋다.

good code

위와 같이 IsSquare 메소드 를 통해서 정사각형인지 유무를 그리고 false 면 나머지는 직사각형이 되기 때문에 위와 같이 간소하게 객체를 설계 할 수 있다.

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

인터페이스 분리 원칙 이란 “클라이언트 기준으로 인터페이스를 분리” 하는 개념을 의미한다.

한 곳에서 인터페이스를 분리해서 사용하는것인데 이것이 분리하는 것이 각각 큰 개념에서 정의할때도 있고 아닐 때도 있지만 여기서 만은 클라이언트 기준으로 각각 개념별로 쪼개서 사용을 해야 된다.

그렇다면 위의 코드를 어떻게 리팩토링 되었을까?

good code

각각 인터페이스를 쪼개서 사용을 해준다. 이렇게 사용을 하면 객체의 기준으로 클라이언트 별로 필요하는 기능을 정의 해서 사용을 하면 된다. 예로 Troll 만 있지만 A 라는 기능을 넣어주세요, 그때 Shoot 만 해주세요 라는 요건이 있다고 가정을 하면 A Class 에서 Shooter 를 상속 받아서 재정의 해주면 된다.

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

의존성 역전 원칙이란 “객체는 저수준 모듈 보다 고수준 모듈에 의존해야 된다.” 는 개념이다. 여기서 고수준의 모듈은 우리가 정의 해 놓은 interface, abstract class 를 의미한다. 저수준의 모듈은 이것을 상속 받아서 직접 구현된 객체이다.

이것을 좀더 구체적으로 정의를 하면 객체 즉 클래스는 구현체에서 의존 되는 것이 아니라 추상된 클래스 거나 인터페이스 에 의존되어야 한다는 것을 말한다.

FEDev Class , BEDev Class 는 단순한 구현체로 정의가 되어 있다, 하지만 이것을 기능서 interface 를 통해서 의존도를 높이는 것이 의존성 역전의 원칙에 개념에 정의되서 리팩토링 하였다.

Developer Interface 에 기능을 정의 해주고 구현체에 상속을 통해서 각각의 개발자가 개발하도록 정의를 해주었다. 그리고 SoftwareProject Class 에서 개발이라는 기능을 호출 해주는 방법으로 사용을 했다. 이렇게 되면서 구현체를 통해서 기능을 정의를 하는 것이 아닌 interface 고수준의 모듈로 호출을 한다.

사실 패턴을 익히고 쓰는것에 대한 부분이 흔히 말하는 개발자가 가져야 되는 기본적인 패턴 인데 많은 부분에서 놓치고 사용을 하는 것 같았다.

또한 어떠한 개발자는 이게 굳이 필요하나 라는 생각 또한 가진 개발자도 있겠지만 적어도 필자는 현존하는 패턴들이 그냥 있는 것이 아니라 개발자들이 유지보수하면서 나온 패턴이라고 생각이되고 필요할때 그 패턴을 사용해서 개발하는 것이 좋다고 생각 된다.

--

--