객체 지향 특성
•
추상화
◦
객체의 공통적인 속성과 기능을 추출하여 정의하는것
◦
코드로 생각한다면 추상 클래스(abstract Class)와 인터페이스(Interface)가 존재
◦
예를 들어 자동차, 오토바이, 자전거가 있다면 공통 적으로 전진, 후진과 같은 공통의 기능을 하나로 묶어 구현해두는 것
→ 추후 자동차, 오토바이, 자전거라는 구현체(Class)를 만든 후 Implement를 통해 구현
•
캡슐화
◦
서로 연관있는 속성과 기능들을 하나의 캡슐(capsule)로 만들어 데이터를 외부로부터 보호하는 것
◦
데이터 은닉(내부의 동작을 감추고 외부에는 필요한 부분만 노출)과 보호(외부로부터 클래스에 정의된 속성과 기능들을 보호)를 목적으로 한다.
◦
접근 제어자와, getter/setter 매서드를 이용하는 방법 2가지가 존재
1.
접근 제어자
접근 제어자 | 클래스 내 | 패키지 내 | 다른 패키지의 하위 클래스 | 패키지 외 |
private | O | X | X | X |
default | O | O | X | X |
protected | O | O | O | X |
public | O | O | O | O |
2.
getter/setter
선택적으로 외부에 접근을 허용할 속성과 그렇지 않을 속성 설정 가능
•
상속
◦
기존의 클래스를 재활용하여 새로운 클래스를 작성하는 자바의 문법 요소
◦
코드로 생각한다면 extends를 통해 기존의 있던 코드들을 사용할 수 있도록 해주는 기능
◦
예를 들어 이동수단이라는 Class를 만들어 전진, 후진, 색상 등을 구현해두고, 오토바이, 자동차 등을 구현할 때 extends를 통해 이동수단 Class의 매서드를 사용할 수 있게 해주는 것
•
다향성(핵심)
◦
어떤 객체의 속성이나 기능이 그 맥락에 따라 다른 역할을 수행할수 있는 객체 지향의 특성
◦
코드론 본다면 인터페이스에 필요한 구현체를 언제든 바꿔끼워도 괜찮다는 의미
◦
예를 들어, 대학로에서 연극을 볼 경우 배역은 정해져 있지만 요일별, 날짜별로 배우들이 바뀌는 데, 여기서 배역을 인터페이스, 배우를 구현체로 보면 이해하기 쉽다. 배역은 항상 그대로지만 구현체인 배우는 언제든 대본이라는 틀에만 맞춰진다면 바뀔 수 있다.
◦
이를 구현하기 위해 오버로딩(Overloading)과 오버라이딩(Overriding)이라는 개념이 사용된다.
다향성으로 인해 인터페이스를 구현한 객체를 실행 시점에 유연하게 변경할 수 있다.(DI, IoC를 이용)
•
클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다.(아래와 같은말로 이해하면 됨)
•
실행 시점에 클라이언트 변경 없이 MemoryMemberRepository를 이용할 지, JdbcMemberRepository를 이용할 지 실행 시점에 결정할 수 있다.
@Bean
public MemberRepository memberRepository(){
// return new MemoryMemberRepository();
return new JdbcMemberRepository(dataSource);
}
Java
복사
오버로딩(Overloading) vs 오버라이딩(Overriding)
•
오버로딩(Overloading)
◦
매서드의 이름이 같아야 함
◦
리턴형은 같아도 되고 달라도 됨
◦
파라미터 개수가 달라야 함
◦
파라미터 개수가 같을 경우 데이터 타입이 달라야 함
→ 리턴타입은 시그니쳐에 포함되지 않으니 주의(컴파일 에러)
•
오버라이딩(Overriding)
◦
오버라이딩 하고자 하는 매서드가 상위 클래스에 존재해야 함
◦
매서드의 이름이 같아야 함
◦
매서드의 파라미터 개수와 데이터 타입이 같아야 함
◦
리턴형이 같아야 함
◦
상위 매서드와 동일하거나 내용이 추가되어야 함
객체 지향 프로그래밍
객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다.(위키)
유연하고 변경이 용이?
- 다형성과 관련이 있는 내용으로 컴퓨터 부품처럼 무언가 바꿔끼울 수 있다는 의미
역할과 구현을 분리(다향성)
•
역할과 구현으로 구분하면 세상이 단순해지고, 유연해지며 변경도 편리해진다.
•
확장 가능한 설계가 가능
•
인터페이스가 변경된다면 다 변경해야 하는 것은 어쩔 수 없기에 인터페이스를 안정적으로 잘 설계하는 것이 중요!!
•
장점
◦
클라이언트는 대상의 역할(인터페이스)만 알면 됨
◦
클라이언트는 구현 대상의 내부 구조를 몰라도 됨
◦
클라이언트는 구현 대상의 내부 구조가 변경되어도 영행받지 않음
◦
클라이언트는 구현 대상 자체를 변경해도 영향 받지 않음
자바 언어의 다향성을 활용
•
역할 : 인터페이스(Interface)
•
구현 : 클래스, 구현체 (Class)
⇒ 객체를 설계할때 역할과 구현을 명확히 분리!!
⇒ 객체 설계시 인터페이스(역할)을 먼저 부여하고, 그 역할을 수행하는 구현 객체 만들기
스프링과 객체 지향
•
다형성이 가장 중요
•
스프링이 다형성을 극대화하여 이용할 수 있게 해줌
•
스프링에서 이야기하는 제어의 역전(IoC), 의존관계 주입(DI)은 다형성을 활용해서 역할과 구현을 편리하게 다룰 수있도록 지원
실무 고민
•
인터페이스를 도입하면 추상화라는 비용 발생
→ 코드를 열어보면, 인터페이스를 보고 구현체를 찾아가 또 한번 찾아야 하는 비용
•
기능을 확장할 가능성이 없다면 구체 클래스로 직접 사용하고 향후 필요할 때 리펙토링을 통해 인터페이스를 도입하는 것도 방법
좋은 객체 지향 설계 5가지 원칙(SOLID)
SRP : 단일 책임 원칙(single responsibility principle)
•
한 클래스는 하나의 책임만 가져야 한다.
•
중요한 기준은 변경, 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙 잘 따른 것(변경할 때 바꾸고자 하는 것 외에 이것저것 바꿀게 많아지면 안된다는 원칙)
•
Ex. UI 변경, 객체 생성과 상용을 분리
OCP : 개방-폐쇄 원칙(Open-Closed principle)
•
소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
•
다형성을 활용해 구현
•
인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현
•
역할과 구현의 분리를 생각
문제점
•
클라이언트가 구현 클래스를 직접 선택해주어야 함
→ 아래와 같이 객체를 변경해주는 것을 말함
@Bean
public MemberRepository memberRepository(){
// return new MemoryMemberRepository();
return new JdbcMemberRepository(dataSource);
}
Java
복사
•
구현 객체를 변경하려면 클라이언트 코드를 변경해야 함
•
분명 다형성을 사용했지만 OCP 원칙을 지킬 수 없음
⇒ 객체를 생성하고 연관관계를 맺어주는 별도의 조립, 설정자가 필요 → 스프링 컨테이너의 역할
LSP : 리스코프 치환 원칙(Liskov substitution principle)
•
프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 함
•
다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것, 다형성을 지원하기 위한 원칙, 인터페이스를 구현한 구현체는 믿고 사용하려면, 이 원칙이 필요
•
인터페이스의 액셀이라는 매서드에다가 후진하는 기능을 구현하지 말라는 의미, 즉 역할에 맞는 구현을 해라!
ISP : 인터페이스 분리 원칙 (Interface segregation principle)
•
특정 클라이언트를 위한 인터페이스 여러개가 범용 인터페이스 한개보다 낫다 → 인터페이스를 구체화 시켜서 세부적으로 작성하라는 의미!
•
자동차 인터페이스 → 운전, 정비 인터페이스로 나누기
•
분리하면 정비 인터페이스 자체가 변해도 운전자 클라이언트에 영향을 주지 않음
•
인터페이스가 명확해지고 대체 가능성이 높아짐
DIP : 의존관계 역전 원칙 (Dependency Inversion principle)
•
프로그래머는 “추상화에 의존해야지. 구체화에 의존하면 안된다.” 의존선 주입은 이 원칙을 따라는 방법 중 하나
•
쉽게 이야기해서 구현 클래스는 의존하지 말고, 인터페이스에 의존해라!!!
문제점
•
위 OCP와 다형성을 설명했을 때 보여준 코드에서 MemberService 클라이언트가 구현 클래스를 직접 선택하게 되어 있다.
→ 이것 역시 DIP 위반이다.
@Bean
public MemberRepository memberRepository(){
// return new MemoryMemberRepository();
return new JdbcMemberRepository(dataSource);
}
Java
복사
다형성 만으로는 구현 객체를 변경할 때, 클라이언트도 함께 변경되기 때문에, 다형성 만으로는 OCP, DIP를 지킬 수 없다.
→ 스프링이 스프링 컨테이너와 DI(의존성 주입) 통해 지킬 수 있게 해준다.
(Spring DI 참고)