👉 객체지향 프로그래밍
- 객체 지향 프로그래밍(영어: Object-Oriented Programming, OOP)은 컴퓨터 프로그래밍의 패러다임 중 하나이다. 객체 지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 "객체"들의 모임으로 파악하고자 하는 것이다. 각각의 객체는 메시지를 주고받고, 데이터를 처리할 수 있다.
객체 지향 프로그래밍은 프로그램을 유연하고 변경이 쉽게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다. 또한 프로그래밍을 더 배우기 쉽게 하고 소프트웨어 개발과 보수를 간편하게 하며, 보다 직관적인 코드 분석을 가능하게 하는 장점이 있다. 그러나 지나친 프로그램의 객체화 경향은 실제 세계의 모습을 그대로 반영하지 못한다는 비판을 받기도 한다. (by 위키)
👉 다형성
- 클라이언트(요청자)를 변경하지 않고, 내부 기능을 변경할 수 있다.
// status의 기능을 변경하고싶다.
// status를 "off" 값을 반환한다.
class Plug {
public void on() {}
public void off() {}
}
interface InternetOfThings {
String status();
}
class SmartPlug extends Plug implements InternetOfThings {
public String status() {
...
...
return "ok";
}
}
class OffPlug extends Plug implements InternetOfThings {
public String status() {
return "off";
}
}
SmartPlug sp = new SmartPlug();
sp.on();
sp.off();
OffPlug op = new OffPlug();
Plug p = sp;
p.on();
p.stop();
InternetOfThings iot = sp; -> InternetOfThings iot = op
iot.status(); // sp -> ok, op -> off
👉 SOLID
- 클라이언트(요청자)를 변경하지 않고, 내부 기능을 변경할 수 있다.
SRP(Single Responsibility Principle) - 단일 책임 원칙
- 한 클래스는 하나의 책임만 가져야 한다.
- 하나의 책임은 클수도 있고, 작을 수도 있다.
- 하나의 책임의 기준은 변경이다.
# 변경이 발생하였을 때, 변경해야 될 부분이 적으면, 단일 책임 원칙을 잘 따른 것이다.
# 클래스를 변경해야하는 이유가 오직 하나여야 한다.
ex) 결제 버튼의 위치가 변경 되었지만, 결제 기능에 대한 영향은 없다.
class Galaxy {
private String serialNumber;
private String cpu;
private String memory;
private int battery;
private double weight;
}
--------------
serialNumber는 고유정보 -> 변화 요소가 아님.
cpu, memory, battery, weight는 특성 정보로 변경이 발생할 수 있음.
특성 정보에 변화가 발생하면, Galaxy 을 변화 시켜야 되는 부담이 발생함.
-------------------------
// 스펙만 관리
class GalaxySpec {
private String cpu;
private String memory;
private int battery;
private double weight;
}
class Galaxy {
private String serialNumber;
private GalaxySpec spec;
public Galaxy(String serialNumber, GalaxySpec spec) {
this.serialNumber = serialNumber;
this.spec = spec;
}
}
OCP(Open/Closed Principle) - 개방/페쇄 원칙
- 확장에는 열려있으나 변경에는 닫혀있어야한다.
- 변경을 위한 비용은 줄이고, 확장을 위한 비용은 극대화 해야한다.
- 객체지향의 장점을 극대화하는 아주 유용한 원리(feat, 다형성)
class Galaxy {
private String serialNumber;
private String cpu;
private String memory;
private int battery;
private double weight;
}
class GalaxySpec {
private String cpu;
private String memory;
private int battery;
private double weight;
}
class Galaxy {
private String serialNumber;
private GalaxySpec spec;
public Galaxy(String serialNumber, GalaxySpec spec) {
this.serialNumber = serialNumber;
this.spec = spec;
}
}
----------
class IPhone {
private String serialNumber;
private String cpu;
private String memory;
private int battery;
private double weight;
}
class IPhoneSpec {
private String cpu;
private String memory;
private int battery;
private double weight;
}
class IPhone {
private String serialNumber;
private IPhoneSpec spec;
public IPhone(String serialNumber, IPhoneSpec spec) {
this.serialNumber = serialNumber;
this.spec = spec;
}
}
class Shaomi {
private String serialNumber;
private String cpu;
private String memory;
private int battery;
private double weight;
}
class ShaomiSpec {
private String cpu;
private String memory;
private int battery;
private double weight;
}
class Shaomi {
private String serialNumber;
private ShaomiSpec spec;
public IPhone(String serialNumber, IPhoneSpec spec) {
this.serialNumber = serialNumber;
this.spec = spec;
}
}
-------------------------------------------------
갤럭시 외에 새로운 핸드폰들이 생겨난다면?
추상화 작업을 통해서, 공통화 하자.
새로운 핸드폰이 추가 되면서, 변경 될 수 있는 부분들을 분리시킴으로써, 코드 수정을 최소화하여,
결합도를 줄이고,응집도를 높혔다.
-------------------------------------------------
class Phone {
private String serialNumber;
private PhoneSpec spec;
public Phone(String serialNumber, PhoneSpec spec) {
this.serialNumber = serialNumber;
this.spec = spec;
}
}
class PhoneSpec {
private String cpu;
private String memory;
private int battery;
private double weight;
}
class Galaxy extends Phone
class IPhone extends Phone
class 샤오미 extends Phone
class Sony extends Phone
- 템플릿 메소드 패턴 찾아볼 것 → OCP원칙의 좋은 예
LSP(Liskov Substitution Principle) - 리스코프 치환 원칙 (한번 더 보고 확인 후에 자료에 정정)
- 하위 타입(자식)은 언제나 자신의 상위 타입(부모)으로 교체할 수 있어야 한다.
class Rectangle {
private int width;
private int height;
public void setHeight(int height) {
this.height = height;
}
public int getHeight() {
return this.height;
}
public void setWidth(int width) {
this.width = width;
}
public int getWidth() {
return this.width;
}
public int getArea() {
return this.width * this.height;
}
}
class Square extends Rectangle {
@Override
public void setHeight(int value) {
this.width = value;
this.height = value;
}
@Override
public void setWidth(int value) {
this.width = value;
this.height = value;
}
}
public class Test {
static void testLSP(Rectangle rectangle) {
rectangle.setWidth(5);
rectangle.setHeight(3);
System.out.println(rectangle.getArea());
}
}
public static void main(String[] args){
Rectangle rectangle = new Rectangle();
rectangle.setWidth(5);
rectangle.setHeight(2);
rectangle.getArea(); // 10
Rectangle square = new Square();
square.setWidth(5);
square.setHeight(2);
square.getArea(); // 25
Test.testLSP(new Rectangle());
Test.testLSP(new Square());
}
- 정확성을 깨뜨리면 안된다.
- 왜 중요한가?
- 원칙을 지키지 않으면 , 자식클래스의 인스턴스를 파라미터로 전달 했을 때 메소드가 이상하게 작동 할 수 있습니다.
# 스피커 기능 중 볼륨 업은 소리를 키우는 기능이다. 하만카돈 스피커의 볼륨업은 소리를 줄이면 안된다. 소리가 작게 커지더라도 소리를 키워야한다. → 그래야 믿고 쓸 수 있다.
- 부모클래스에 대해 작성된 단위테스트가 자식클래스에 대해서는 작동되지 않을 것입니다.
- 상속을 잘 활용하고 있다면, 이미 LSP를 하고 있다
ISP(Interface segregation Principle) - 인터페이스 분리 원칙
- 클라이언트가 자신이 사용하지 않는 메서드에 의존하지 않아야 한다.
- 특정 클라이언트를 위하여, 하나의 범용 인터페이스를 제공하는 것 보다 여러 개의 인터페이스를 제공하는 것이 났다.
코드(AS-IS)
public interface Phone {
void call(String phoneNumber);
void pay(String cardName);
void wirelessCharge();
}
public class Galaxy implements Phone {
@Override
public void call(String phoneNumber) {
// ..
}
@Override
public void pay(String cardName) {
// ..
}
@Override
public void wirelessCharge() {
// ..
}
}
public class IPhone implements Phone {
@Override
public void call(String phoneNumber) {
// ..
}
@Override
public void pay(String cardName) {
// ..
}
@Override
public void wirelessCharge() {
// ..
}
}
public class SkyPhone implements Phone {
@Override
public void call(String phoneNumber) {
// ..
}
@Override
public void pay(String cardName) {
// ..
}
@Override
public void wirelessCharge() {
// 1. 가만히 냅눈다. - 극강 빌런
// 2. System.out.println("사용하지 않음"); - 그나마 양호
// 3. throw new NotSupporedException(); - 테스트코드에서
}
}
public void main (String args[]) {
Phone phone = new Galaxy();
phone = new Galaxy();
phone = new SkyPhone();
phone.wirelessCharge(); // 1. 에러를 찾는데, 시간이 오래 걸린다. 이건 빌런의 소행이다.
}
DIP(Dependency Inversion Principle) - 의존관계 역전 원칙
- 객체들 간의 협력 하는 과정에서 의존 관계가 형성 된다.
- 의존관계 역전 원칙은 이러한 의존 관계를 맺을 때, 어떻게 하면 변화에 용이하게 대응할 수 있을 것인가에 대한 가이드 라인이다.
- 변하기 쉬운 것과 어려운 것을 구분해야한다.
# 변하기 쉬운 것 (구체적인 행동)
ex) 스마트폰으로 전화를 건다.
ex) 공중전화로 전화를 건다.
ex) 이메일을 발송한다.
ex) 카카오톡 메시지를 전달한다.
# 변하기 어려운 것 (흐름이나 개념과 같은 추상적인 것)
ex) 전화를 건다
ex) 메시지를 전달한다.
public interface Phone {
void call(String phoneNumber);
}
public class SmartPhone implements Phone {
@Override
public void call(String phoneNumber) {
System.out.println("스마트폰 : " + phoneNumber);
}
}
public class PublicPhone implements Phone {
@Override
public void call(String phoneNumber) {
System.out.println("공중 전화 : " + phoneNumber);
}
}
public class InternetPhone implements Phone {
@Override
public void call(String phoneNumber) {
System.out.println("인터넷 전화 : " + phoneNumber);
}
}
public class Person {
private Phone phone;
public void setPhone(Phone phone) {
this.phone = phone; // 폰이 계속 바뀜.
}
public void call(String phoneNumber) {
phone.call(phoneNumber);
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person();
person.setPhone(new SmartPhone());
person.setPhone(new InternetPhone());
// 코드 수정 X
person.call("01012341234"); // 스마트폰 전화,
}
}
🙋♂️ 소감 :
빌런은 되지 말아야지...^^;
😈 아는 내용이라고 그냥 넘어가지 않기! 😈
'❤️🔥TIL (Today I Learned)' 카테고리의 다른 글
[TIL] 2022-11-23(18day) (0) | 2022.11.23 |
---|---|
[TIL] 2022-11-22(17day) (0) | 2022.11.22 |
[TIL] 2022-11-18(15day) (0) | 2022.11.18 |
[TIL] 2022-11-17(14day) (0) | 2022.11.17 |
[TIL] 2022-11-16(13day) (0) | 2022.11.16 |
댓글