[Java] 객체지향 프로그래밍

김하밍·2023년 7월 8일

CS 스터디

목록 보기
5/11

명령형 프로그래밍

무엇(What)을 할 것인지 나타내기보다 어떻게(How) 할 건지를 설명하는 방식

  • 객체지향 프로그래밍: 객체들의 집합으로 프로그램의 상호작용을 표현 (C++, Java, C#)
  • 절차지향 프로그래밍: 수행되어야 할 순차적인 처리 과정을 포함하는 방식 (C, C++)

객체지향 프로그래밍


많은 객체들이 모여서 상호 협력하면서 데이터를 처리하는 방식입니다. 설계에 많은 시간이 소요되며 처리 속도가 다른 프로그래밍 패러다임에 비해 상대적으로 느립니다.

하지만, 그만큼 프로그램을 묶음 단위로 잘게 쪼개어 추후에 가져다 쓰기 편하게 만들어 놓은 프로그래밍 방식입니다.

객체지향 프로그래밍의 특징


  • 추상화 (abstraction)
    복잡한 시스템으로부터 핵심적인 개념 또는 기능을 간추려내는 것 입니다.
    사람1 의 특징: 교사, 팀장, 키 160, 안경 안씀, 게임 못함
    이 중에서 일부분의 특징을 핵심으로 여겨 아래와 같이 간추립니다.
    교사, 팀장

  • 캡슐화
    객체의 속성과 메서드를 하나로 묶고 일부를 외부에 감추어 은닉하는 것 입니다.

  • 상속성 (inheritance)
    상위 클래스의 특성을 하위 클래스가 이어받아서 재사용/ 추가/ 확장하는 것을 말합니다.
    상속을 사용하기 위해서는 extends 키워드를 상속받을 클래스에 명시하여 사용합니다.

  • 다형성 (polymorphism)
    클래스가 상속 관계에 있을 때, 하나의 메서드나 클래스가 다양한 방법으로 동작하는 것을 말합니다.

대표적으로 오버로딩, 오버라이딩이 있습니다.

  • 오버로딩: 서로 다른 메서드 시그니처를 갖는 여러 메소드를 같은 이름으로 정의하는 것 (컴파일 시 발생하는 정적 다형성)
`
`
// 메서드명 eat, 매개변수 1개
public void eat(String a) {
System.out.println("I eat " + a);
}

// 메서드명 eat, 매개변수 2개
public void eat(String a, String b) {
System.out.println("I eat " + a + "and " + b);
}
`
`

오버로딩 적용 후: 메서드명은 같지만, 메서드의 개수에 따라 다른 함수가 호출되고 있습니다.

  • 오버라이딩: 상속 받은 메서드의 내용만 변경하여 덮어씌운 것 (런타임 중 발생하는 동적 다형성)
class Animal {
	public void bark() {
    	System.out.println("mumu! mumu!");
    }
}

class Dog extends Animal {
	@Override
	public void bark() {
		System.out.println("wal! wal!");
  	}
}
public class Main {
	public static void main(Sting[] args) {
		Dog d = new Dog();
		d.bark();
	}
}
/*
wal! wal!
*/

오버라이드 적용 후: 부모 클래스는 mumu! mumu! 로 짖게 만들었는데, 자식 클래스에서 wal! wal! 로 짖게 만들었더니 자식 클래스 기반으로 메서드가 재정의 되었습니다.

설계 원칙

객체지향 프로그래밍을 설계할 때는 SOLID 원칙을 지켜주어야 합니다.


S: 단일 책임 원칙 (SRP, Single Responsibility Principle)

  • 모든 클래스는 각자 하나의 책임만 가져야 하는 원칙입니다.
    즉, 하나의 클래스는 하나의 기능을 담당하여 하나의 책임을 수행하는데 집중되도록 하고, 클래스를 따로따로 여러개 설계하라는 원칙입니다.

  • 한 객체에 책임의 갯수가 많아질수록 클래스 내부에서 서로 다른 역할을 수행하는 코드끼리 강하게 결합될 가능성이 높아지게 됩니다. 그래서 그 객체가 하는 기능 하나에 변경사항이 생기면 다른 기능들의 코드까지 모두 다시 살펴보고 테스트를 해야할 수도 있는 문제가 발생합니다.

  • 클래스가 많아져서 코드가 길어짐과 복잡함은 비례하지 않으며, 오히려 이렇게 분리하여 한 클래스가 한 가지 책임에 관한 변경사항이 생겼을 때만 코드를 수정하게 되는 구조가 좋은 구조입니다.

O: 개방-폐쇄 원칙 (OCP, Open Losed Principle)

  • 유지 보수 사항이 생긴다면 코드를 쉽게 확장할 수 있도록 하고, 수정할 때는 닫혀 있어야 하는 원칙입니다.
    쉽게 확장: 새로운 변경 사항 발생 시 => 유연하게 코드를 추가함으로써 큰 힘을 들이지 않고 애플리케이션의 기능을 확장할 수 있어야 함
    수정 닫기: 새로운 변경 사항 발생 시 => 객체를 직접적으로 수정하는 것 제한해야 함
    결론: 추상화 사용을 통해 관계 구축을 권장함

L: 리스코프 치환 원칙 (LSP, Liskov Substitution Principle)

  • OCP 원칙을 따르는 것만으로는 시스템의 한 부분의 변경이 시스템의 다른 부분을 손상시키지 않도록 보장하기에는 충분하지 않을 수 있습니다.

  • 이 리스포크 치환 원칙은 개방 폐쇄 원칙의 확장입니다.
    이 원칙은 다른 부분을 손상시키지 않고 시스템의 한 부분을 변경할 수 있도록 올바른 방식으로 OCP를 구현하는 데 도움이 됩니다. 아키텍처에서 선택한 기본 클래스와 하위 클래슥 동일한 계층에 속하는지 여부를 결정하는 데 도움이 됩니다.

  • 상위 클래스를 하위 클래스 중 하나로 대체하더라도 코드가 여전히 예상대로 작동하는 방식으로 클래스의 계층 구조를 설계해야 하는 원칙입니다.

  • 서브 객체는 언제나 부모 객체로 교체하더라도 시스템이 문제없이 돌아가야 한다는 것을 의미합니다.

  • 다형성 원리를 이용하기 위한 원칙 개념입니다.
    다형성 이용을 위해 부모 타입으로 메서드를 실행해도 의도대로 실행되도록 구성해줘야하는 원칙입니다.

  • 자바 Collection 인터페이스를 예로 들 수 있습니다.
    Collection 타입의 객체에서 자료형을 LinkedList 에서 전혀 다른 자료형 HashSet으로 변경하여 add() 메서드 실행해도 원래 의도대로 작동되기 때문입니다.

I: 인터페이스 분리 원칙 (ISP, Interface Segregation Principle)

  • ISP는 인터페이스의 단일 책임을 강조하는 것 입니다.
    인터페이스를 사용하는 클라이언트를 기준으로 분리함으로써, 클라이언트의 목적과 용도에 적합한 인터페이스만을 제공하는 것이 목표인 원칙입니다.
  • 인터페이스는 제약 없이 자유롭게 다중 상속(구현)이 가능하기 때문에, 분리할 수 있으면 분리하여 각 클래스 용도에 맞게 implements 하라는 설계 원칙이라고 이해하면 됩니다.

D: 의존 역전 원칙 (DIP, Dependency Inversion Principle)

  • 상위 계층은 하위 계층의 변화에 대한 구현으로부터 독립해야 합니다.
  • 구현 클래스에 의존하지 않고, 인터페이스에 의존하라는 원칙입니다.
  • 의존 역전 원칙의 지향점은 각 클래스간의 결합도를 낮추는 것 입니다.

참고

객체지향 프로그래밍
객체지향 프로그래밍의 특징

profile
나만의 언어로 기록하며 성장하기 !

0개의 댓글