📜 Java
:: ✍ 실시간 강의 | 객체지향 프로그래밍
:: 객체지향 프로그래밍 ( Object-Oriented Programming, OOP )
- 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러개의 독립된 단위
- 유연하고 변경이 쉽게 만듦
:: 유연하고 변경이 쉽게
다형성
- 클라이언트(요청자)를 변경하지 않고, 내부 기능을 변경 가능
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();
- 유비쿼터스 언어 = 언어의 합을 맞추는 것
- 솔리드 = 어떻게 낮은비용으로 교체하나?
- 의미있는 메소드를 통해서만 건드릴 수 있게 단순 set으로 쓰지말고
set이라고 하면 구체적으로 어떤 행동인지 알기가 힘드니까 구체적으로 명시
:: SOLID
SRP(Single Responsibility Principle) - 단일 책임 원칙
- 한 클래스는 하나의 책임만 가져야 함
- 하나의 책임은 클수도 있고, 작을 수도 있음
- 하나의 책임의 기준은 변경!
- 변경이 발생하였을 때, 변경해야 될 부분이 적으면, 단일 책임 원칙을 잘 따른 것
- 클래스를 변경해야하는 이유가 오직 하나여야 함
- 예) 결제 버튼의 위치가 변경 되었지만, 결제 기능에 대한 영향은 없다.
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;
}
}
OCP(Open/Closed Principle) - 개방/페쇄 원칙
- 확장에는 열려있으나 변경에는 닫혀있어야 함
- 변경을 위한 비용은 줄이고, 확장을 위한 비용은 극대화 해야함
- 객체지향의 장점을 극대화하는 아주 유용한 원리(feat, 다형성)
- 템플릿 메소드 패턴 찾아볼 것 → OCP원칙의 좋은 예
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
LSP(Liskov Substitution Principle) - 리스코프 치환 원칙
- 부모 객체와 이를 상속한 자식 객체가 있을 때 부모 객체를 호출하는 동작에서 자식 객체가 부모 객체를 완전히 대체할 수 있다는 원칙
- 하위 타입(자식)은 언제나 자신의 상위 타입(부모)으로 교체할 수 있어야 함
- 정확성을 깨뜨리면 안됨
- 원칙을 지키지 않으면 , 자식클래스의 인스턴스를 파라미터로 전달 했을 때 메소드가 이상하게 작동 할 수 있음
- 스피커 기능 중 볼륨 업은 소리를 키우는 기능이다. 하만카돈 스피커의 볼륨업은 소리를 줄이면 안된다. 소리가 작게 커지더라도 소리를 키워야한다. → 그래야 믿고 쓸 수 있다.
- 부모클래스에 대해 작성된 단위테스트가 자식클래스에 대해서는 작동되지 않을 것
- 상속을 잘 활용하고 있다면, 이미 LSP를 하고 있는 것
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();
Rectangle square = new Square();
square.setWidth(5);
square.setHeight(2);
square.getArea();
Test.testLSP(new Rectangle());
Test.testLSP(new Square());
}
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() {
}
}
public void main (String args[]) {
Phone phone = new Galaxy();
phone = new Galaxy();
phone = new SkyPhone();
phone.wirelessCharge();
}
public interface Phone {
void call(String phoneNumber);
}
public interface WirelessCharge {
void wirelessCharge();
}
public interface Payment{
void pay(String cardName);
}
public class Galaxy implements Phone, WirelessCharge, Payment {
@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) {
}
}
DIP(Dependency Inversion Principle) - 의존관계 역전 원칙
- 객체들 간의 협력 하는 과정에서 의존 관계가 형성
- 의존관계 역전 원칙은 이러한 의존 관계를 맺을 때, 어떻게 하면 변화에 용이하게 대응할 수 있을 것인가에 대한 가이드 라인
- 변하기 쉬운 것과 어려운 것을 구분해야 함
- 변하기 쉬운 것
- 구체적인 행동
- 스마트폰으로 전화를 건다.
- 공중전화로 전화를 건다.
- 이메일을 발송한다.
- 카카오톡 메시지를 전달한다.
- 하기 어려운 것
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());
person.call("01012341234");
}
}