[Java] 객체 지향 설계 SOLID 원칙

김동욱·2023년 8월 21일
0

Java

목록 보기
4/8
post-thumbnail

소프트웨어는 하드웨어와는 다르게 수시로 수정되며 그 길이가 길어질수록 유지 보수는 더욱 힘들어집니다.
그렇기 때문에 코드를 관리함에 있어서 코드를 잘 분류하고 때에 따라서는 특정 모듈을 완전히 교체하더라도 불편함이 없어야 합니다.

이러한 문제점들을 해결하기 위한 최적의 방법이 객체 지향 프로그래밍입니다.
객체 지향 프로그래밍이란 시스템을 구성하는 구성 요소를 객체단위로 구분하여 이 객체들 사이의 상호작용을 중심으로 설계하는 방식을 말합니다.
객체들의 모임으로 이루어져 있기 때문에 변경에 용이하고 유연하며 캡슐화, 상속, 다형성 등 다양한 특징을 가지고 있습니다.

오늘은 이 객체 지향 설계에 있어서 중요한 5가지 원칙에 대해 알아보겠습니다.

SOLID

SRP : 단일 책임 원칙

OCP : 개방 폐쇄 원칙

LSP : 리스코프 치환 법칙

ISP : 인터페이스 분리 원칙

DIP : 의존성 역전 원칙

SRP: 단일 책임 원칙

한 클래스에는 단일의 책임만 가져야 한다는 원칙입니다.
프로젝트 규모가 작으면 한개의 클래스에 다양한 기능을 넣더라도 당장은 불편함이 없고 오히려 더 편하게 느껴지지만 규모가 커지면 코드의 복잡성이 증가하고 유지보수와 재사용 불가 등 다양한 문제점이 발생합니다.

OCP: 개방 폐쇄 원칙

확장에는 열려있고 변경에는 닫혀야 한다는 원칙입니다.
즉 기능을 변경하지 않고 기존 기능을 확장(추가)시키는 게 더 유리하다는 의미입니다.

예를 들어
카드 결제와 현금 결제가 있는 결제 서비스에 새로운 결제 수단을 추가하는 경우를 생각해봅시다.
카드 결제와 현금 결제가 있던 클래스에 새로운 네이버페이 결제를 추가해주게 되면 기존 소스코드를 변경했기 때문에 코드 변경이 필요하고 또한 새로운 결제 수단을 추가할 때 마다 똑같이 소스코드를 수정해야하는 상황이 생기게 됩니다.

OCP를 지키게 되면 결제 수단에 대한 인터페이스를 따로 구현하고 새로운 결제 클래스를 추가해주어 기존 코드를 수정하지 않도록 해줍니다.

예시

[OCP를 지키지 않은 코드]

public class PaymentSerivce{
	public void cardPay(int amount){ ''' }
    public void cashPay(int amount){ ''' }
  
    //추가된 네이버 페이, 해당 클래스를 직접 수정해줘야 한다.
    public void naverPay(int amount){ ''' } 
   }

[OCP를 지킨 코드]

public interface PaymentMethod{
	void pay(int amount); //
}
public class CardPayment inplement PaymentMethod{
	@Override
    public void pay(int amount){ ''' }
}
public class CashPayment inplement PaymentMethod{
	@Override
    public void pay(int amount){ ''' }
}
    //추가된 네이버 페이, 새로운 클래스를 추가해주면 된다 
public class NaverPayment inplement PaymentMethod{
	@Override
    public void pay(int amount){ ''' }
}

LSP: 리스코프 치환 법칙

서브 타입은 언제나 기반 타입으로 교체할 수 있어야 한다는 원칙입니다.
즉 자식 클래스에서 부모 클래스로 변경해도 문제가 없어야 합니다.

만약에 특정 자식 클래스에만 존재하는 기능들이 존재한다면 그 기능은 그 특정 자식 클래스에만 강하게 의존하기 때문에 유지보수와 재활용에 좋지 않습니다.
그렇기 때문에 부모 인터페이스를 통해 재정의한 기능들만 사용해서 일관성을 유지해야합니다.

ISP: 인터페이스 분리 원칙

인터페이스도 단일 책임을 갖도록 분리해야 한다는 원칙입니다.
한개의 인터페이스에 다양한 기능들을 넣어 구현하게 되면 구현체안에서 사용되지 않는 기능들이 발생하게 됩니다.

예를 들어 결제 서비스 구현을 위한 결제 인터페이스에 주문하기, 팁 주기처럼 모든 기능을 넣어두면 구현체는 상속하는 인터페이스의 모든 기능을 구현해야하므로 우리나라에서는 굳이 필요없는 팁 주기 메서드가 빈 메서드인 상태로 남아있게됩니다.
즉 팁 인터페이스, 주문 인터페이스 처럼 구분해놓으면 구현체들은 필요한 기능들만 상속받아 사용할 수 있게 됩니다.

DIP: 의존성 역전 원칙

서비스가 클래스를 바라보는 것이 아니라 인터페이스를 바라봐야 합니다.
즉 하위 모듈에 의존하게 되면 변경이 어려우니 중간에 인터페이스를 둬서 변경에 용이하도록 해줍니다.

profile
안녕하세요. 공부해요

0개의 댓글