java study #2 OOP Principal

김태준·2023년 6월 21일
0

java Study

목록 보기
2/3
post-thumbnail

객체 지향 언어에 있어 객체 지향 설계 원칙인 SOLID 원칙을 지키며 코드 구현을 해야 한다.
SOLID 원칙을 통해 우리는 객체지향과 클래스가어떤 구조를 갖는지, 상속이 왜 존재하는지를 알 수 있다.
또한, SOLID 설계 원칙을 학습함으로써 OOP 특징인 (추상화, 상속, 다형성, 캡슐화)와 함께 알아두면 면접 대비에도 좋고 디자인 패턴 학습에도 도움이 된다.

가장 중요한 것은 아래 2가지.

  • 객체 지향의 목표는 유지보수를 용이하게 하는 것이다.
  • 객체 3요소 : 협력, 역할, 책임

SOLID의 종류로는 다음과 같다.

✅ SRP - 단일 책임 원칙

  • 클래스는 단 한 개의 책임만을 가져야 한다. (객체 크기가 커짐 막아줌)
  • 클래스를 변경하는 이유는 단 하나여야 한다.
  • 이를 지키지 않는다면 책임 변경에 의해 타 책임과 관련한 코드에 영향을 끼치게 되므로 유지보수가 매우 비효율적이다.

이때 책임이란, 기능으로 생각하면 된다. 즉, 한 클래스 내 수행할 수 있는 기능(책임)이 여러 개인 경우 클래스 내 함수끼리 강한 결합을 발생할 가능성이 높아지게 된다. 즉 한 클래스 내에 기능이 여러 개인 경우 반드시 유지보수를 위해 수정 작업을 거쳐야 하는데, 이는 곧 비효율적인 방식이 된다.

예시로 seek이라는 클래스 내 A 메소드가 있고, A메서드의 결과를 기반으로 B메서드를 호출하고 B메서드 기반으로 C메서드를 호출한다면 만일 A 메서드 동작 수정이 필요한 경우 B, C에도 영향을 미치므로 이는 기능을 분리해줄 필요가 있다.

✅ OCP - 개방 폐쇄 원칙

확장에는 열려있어야 하고 변경에는 닫혀 있어야 한다.
즉, 기존 코드를 변경하지 않고 기능을 수정하거나 추가할 수 있도록 설계해야 한다.
이를 지키지 않는다면 instanceof와 같은 연산자를 사용하거나 다운 캐스팅이 발생한다.

어떤 모듈의 기능에 수정이 필요하다면 그 모듈을 이용하는 타 모듈들을 고치기 보다는 기존 코드 변경 없이 새로운 기능을 생성하거나 변경할 수 있도록 자주 변화하는 부분을 추상화하는 것이 핵심이다.
< 예시 >

class MusicPlayer{
	// playAlgorithm 인터페이스를 변수로 만들기
	private playAlgorithm file;
    public void setFile(playAlgorithm file) {
    	this.file = file;
    }
    // 인터페이스를 상속받아 구현된 클래스 내 play함수 실행
    public void play() {
    	file.play();
    }
}
public class Client{
	// 메인 함수에서 객체 생성 수 지정 및 play
	public static void main(String[] args) {
    	// 객체 생성
        MusicPlayer mp = new MusicPlayer();
       	// 원하는 재생 파일 선택해 play
        mp.setFile(new MP3());
        mp.setFile(new Wav());
        mp.play();
    }
}
        

✅ LSP - 리스코프 치환 원칙

  • 상위 타입 객체를 하위 타입 객체로 치환해도 정상적으로 동작해야 한다.
  • 리스코프 치환 원칙을 지키지 않는 경우 OCP도 지켜지지 않음.
  • 상속관계에서 꼭 일반화 관계가 성립해야 함.
    -> 하위 클래스는 언제나 상위 클래스로 교체할 수 있어야 함.
    -> 상위 클래스의 Override를 따져가며 진행해야 함.
    -> 즉, 상속하는 경우 상위 클래스에서 구현한 원칙을 그대로 따라야 한다. (상위 클래스 내 변수가 모두 int 타입인데, 하위 클래스에선 갑자기 char, boolean인 경우)
    < 예시 >
// Animal 클래스 생성
abstract class Animal {
}
// 말할 수 있는 동물들에 한해 speakable 인터페이스 생성
interface Speakable {
    void speak();
}
// Cat 하위 클래스 생성 및 인터페이스 구현
class Cat extends Animal implements Speakable {
    public void speak() {
        System.out.println("냐옹");
    }
}
// Dog 하위 클래스 생성 및 인터페이스 구현
class dog extends Animal implements Speakable  {
    public void speak() {
        System.out.println("멍멍");
    }
}
// Fish 하위클래스 생성 - speakable 인터페이스 구현할 필요 X
class Fish extends Animal {
}

✅ ISP - 인터페이스 분리 원칙

  • 클라이언트는 자신이 사용하는 메서드에만 의존해야 함.
  • 하나의 통상적인 인터페이스가 아닌 여러 개의 구체적인 인터페이스를 만들어 효율성 높이기 (객체 크기 커지는 것 방지)
  • 각 클라이언트가 필요로 하는 인터페이스를 분리함으로써 클라이언트가 사용하지 않는 인터페이스에 변경이 생겨도 영향이 최대한 없도록 만드는 것이 핵심.
    -> 즉 자신이 사용하지 않는 인터페이스에는 영향을 받아서는 안된다는 것을 의미
    -> 하위 클래스 내 인스턴스는 상위 객체 참조 변수에 대입하여 인스턴스 역할을 하는데 문제가 없어야 한다.

✅ DIP - 의존 역전 원칙

의존 관계 시 변하기 쉬운것 (구체적인 것) 보다는 변하기 어려운 것 (추상적인 것)에 의존해야 한다.

  • 구체화된 클래스 보단, 추상적인 클래스나 인터페이스에 의존할 것.
  • 고수준 모듈을 저수준 모듈 구현에 의존 X
  • 저수준 모듈이 변경되도 고수준 모듈은 변경 필요 X
  • OCP Suport
profile
To be a DataScientist

0개의 댓글