1. 소프트웨어의 가치
- 소프트웨어는 시간에 지남에 따라 변화하는 요구사항에 적응할 필요가 있다.
- 따라서 소프트웨어 개발자는 새로운 가치를 제공할 수 있고, 변환하는 요구사항에 적응할 수 있는 소프트웨어를 만들 필요가 있다.
- 변화에 적응하는 소프트웨어의 특징 3가지
- 유연성
- 확장성
- 유지 보수성
2. 의존
- 사전적 정의: 어떠한 일을 자신의 힘으로 하지 못하고 다른 어떤 것의 도움을 받아 의지한다.
- 코드에서 표현되는 의존
- 객체 참조에 의한 연관 관계
- 메서드 리턴타입이나 파라미터로서의 의존 관계
- 상속에 의한 의존 관계
- 구현에 의한 의존 관계
/* 1. 객체 참조에 의한 연관 관계 */
class Car {
constructor(brand) {
this.brand = brand;
}
startEngine() {
console.log(`${this.brand}의 엔진을 시작합니다.`);
}
}
class Driver {
constructor(name) {
this.name = name;
this.car = null;
}
setCar(car) {
this.car = car;
}
drive() {
if (this.car) {
console.log(`${this.name}이(가) ${this.car.brand}을 운전합니다.`);
}
}
}
// 객체 생성
const myCar = new Car("Toyota");
const john = new Driver("John");
// 의존 관계 설정
john.setCar(myCar);
// 운전
john.drive();
/* 2. 메서드 리턴 타입이나 파라미터로서의 의존 관계 */
class Car {
constructor(brand) {
this.brand = brand;
}
startEngine() {
console.log(`${this.brand}의 엔진을 시작합니다.`);
}
}
class Logger {
log(message) {
console.log(message);
}
}
class Driver {
constructor(name) {
this.name = name;
this.car = null;
this.logger = logger;
}
setCar(car) {
this.car = car;
}
drive() {
if (this.car) {
this.logger.log(`${this.name}이(가) ${this.car.brand}을 운전합니다.`);
this.car.startEngine();
}
}
}
// 객체 생성
const myCar = new Car("Toyota");
const logger = new Logger();
const john = new Driver("John");
// 의존 관계 설정
john.setCar(myCar);
// 운전
john.drive();
/* 3.상속에 의한 의존 관계 */
class Car {
constructor(brand) {
this.brand = brand;
}
startEngine() {
console.log(`${this.brand}의 엔진을 시작합니다.`);
}
}
class Driver extends Car {
constructor(name) {
super(); // 부모 클래스의 생성자 호출
this.name = name;
}
drive() {
if (this.brand) {
console.log(`${this.name}이(가) ${this.brand}을 운전합니다.`);
this.startEngine(); // 상속받은 부모 클래스의 메서드 호출
}
}
}
// 객체 생성
const john = new Driver("John");
john.brand = "Toyota"; // 부모 클래스의 속성 설정
// 운전
john.drive();
/* 4. 구현에 의한 의존 관계 */
// 인터페이스 정의
class Drivable {
drive() {
throw new Error("Not implemented");
}
}
class Car extends Drivable {
constructor(brand) {
super();
this.brand = brand;
}
startEngine() {
console.log(`${this.brand}의 엔진을 시작합니다.`);
}
drive() {
this.startEngine();
console.log(`${this.brand}을 운전합니다.`);
}
}
class Driver {
constructor(name, drivable) {
this.name = name;
this.drivable = drivable;
}
setDrivable(drivable) {
this.drivable = drivable;
}
performDrive() {
if (this.drivable) {
console.log(`${this.name}이(가) 운전을 시작합니다.`);
this.drivable.drive();
} else {
console.log(`${this.name}은(는) 운전 가능한 차량이 없습니다.`);
}
}
}
// 객체 생성
const myCar = new Car("Toyota");
const john = new Driver("John");
// 의존 관계 설정
john.setDrivable(myCar);
// 운전
john.performDrive();
- 따라서 의존이란 두 클래스 간의 관계, 모듈 간의 연결. 관계에 있는 모듈 중 하나가 어떤 용도를 위해 사용하는 것.
- 문제점
- 변경 점에 대한 전파 가능성이 있다는 것이다.
==> 따라서 필요한 의존성만 유지하고, 의존성은 최소화 할 필요가 있다. - 객체 지향에서 되도록 피해야하는 의존은 두 가지다.
==> 의존을 많이 하는 객체: 의존 대상 객체 중 하나라도 변경되면 이 객체는 영향을 받게 됨
==> 많은 수의 객체들의 의존 대상이 되는 객체: 많은 객체들에 영향을 미침
- 변경 점에 대한 전파 가능성이 있다는 것이다.
3. 절차지향 프로그래밍과 객체지향 프로그래밍의 차이
- 절차지향
- 프로시저에 중점을 둔다.
- 프로그램은 일련의 절차적 단계로 구성되고, 데이터와 프로시저가 별도로 존재한다.
- 객체지향
- 데이터와 기능을 하나의 객체로 묶는다.
- 예시 1
function main() {
let accountBalance = 10000;
// Deposit
accountBalance = deposit(accountBalance, 5000);
// Withdraw
accountBalance = withdraw(accountBalance, 3000);
// Print Balance
print(accountBalance);
}
function deposit(balance, amount) {
return balance + amount;
}
function withdraw(balance, amount) {
if (balance < amount) {
console.log("잔액이 부족합니다.");
return balance;
} else {
return balance - amount;
}
}
function print(value) {
console.log("잔액: " + value);
}
// Run the main function
main();
==> 순차적으로 입금, 출급, 전액 출력의 절차를 진행한다.
class BankAccount {
constructor(initialBalance) {
this.balance = initialBalance;
}
deposit(amount) {
this.balance += amount;
}
withdraw(amount) {
if (this.balance < amount) {
console.log("잔액이 부족합니다.");
} else {
this.balance -= amount;
}
}
printBalance() {
console.log("잔액: " + this.balance);
}
}
function main() {
const account = new BankAccount(10000);
// Deposit
account.deposit(5000);
// Withdraw
account.withdraw(3000);
// Print Balance
account.printBalance();
}
// Run the main function
main();
==> BankAccount 클래스로 객체화
==> 객체는 잔액 상태와 입금, 출금, 잔액 출력이라는 기능을 가지고 있다.
- 예시 1에 "VIP 고객은 이체 수수료를 면제해준다를 적용"
function main() {
const accountBalance = 10000;
const bankFee = 1000;
const isVIP = true;
// Deposit
accountBalance = deposit(accountBalance, 5000);
// bankFee 적용
if (!isVIP) {
accountBalance = applyBankFee(accountBalance, bankFee);
}
// Withdraw
accountBalance = withdraw(accountBalance, 3000);
// bankFee 적용
if (!isVIP) {
accountBalance = applyBankFee(accountBalance, bankFee);
}
// Print Balance
print(accountBalance);
}
function deposit(balance, amount) {
return balance + amount;
}
function withdraw(balance, amount) {
if (balance < amount) {
console.log("잔액이 부족합니다.");
return balance;
} else {
return balance - amount;
}
}
function print(value) {
console.log("잔액: " + value);
}
// Run the main function
main();
class BankAccount {
constructor(balance, isVip) {
this.balance = balance;
this.isVip = isVip;
this.bankFee = 1000;
}
deposit(amount) {
this.balance += amount;
if (!this.isVip) {
this.applyBankFee();
}
}
applyBankFee() {
this.balance -= this.bankFee;
}
withdraw(amount) {
if (this.balance < amount + (this.isVip ? 0 : this.bankFee)) {
console.log("잔액이 부족합니다.");
} else {
this.balance -= amount;
if (!this.isVip) {
this.applyBankFee();
}
}
}
printBalance() {
console.log(this.balance);
}
}
function main() {
const account = new BankAccount(10000, true);
// Deposit
account.deposit(5000);
// Withdraw
account.withdraw(3000);
// Print Balance
account.printBalance();
}
// Run the main function
main();
==> 객체지향 설계를 통해서 의존을 다룰 수 있으며 변경이 전파되는 것을 제한하도록 돕는다.
==> 객체는 자체 상태와 행동을 갖기 때문에 가능하며 하나의 객체(내부)가 변경되더라도, 외부에서는 알 수 없다.
4. 객체지향 핵심 이해
- 앨런 케이가 말하는 객체지향 핵심
- Message Passing
- Encapsulation
- Dynamic Binding
- 클라이언트 입장에서 생각하기
- 클라이언트 요청 => 클라이언트는 서버에서 어떤걸 처리하는지 관심도 없으며 그저 응답을 주기를 원한다.
- 서버 ==> 클라이언트는 자신이 원하는 목적을 달성할 수 있는 서버의 API는 알고 있다. 따라서 서버는 할 수 있는 방법으로 처리한다.
- 따라서 메시지를 전송하는 전송자, 메세지를 수신하는 수신자 둘다 서로가 어떤 일을 하는지 알 필요가 없다.
- 캡슐화
: 객체의 내부 상태와 동작을 외부로부터 숨기는 방법- 결합도를 낮추는 것 => 변경을 더 쉽게 할 수 있다.
- 자율적인 객체 => 소통은 인터페이스로, 구현은 내 마음대로 바꿀 수 있다.
- 동적 바인딩 (Dynamic Binding)
: 런타임 시점에 참조 변수와 실제 객체 타입을 확인하여 함수를 호출하는 방식 - 다형성
: 하나의 참조 변수로 여러 개의 객체를 참조할 수 있는 특성
==> 동적 바인딩은 다형성이 적용된 코드에서 발생하는 하나의 현상 - 객체지향에서 다형성을 해석
==> 다른 객체에게 보내는 메시지가 실제로 어떤 메서드를 호출할지 런타임에 결정된다는 의미
5. 객체의 협력, 책임, 역할 개념 이해
- 협력
- 여러 객체가 함께 작업하여 시스템의 기능을 수행하는 것을 의미
- 객체 간의 상호 작용으로 정보를 교환하고, 서비스를 제공하거나 받아들이는 등의 협력 관계를 형성
- 시스템의 전반적인 기능은 객체 간의 협력에 의해 이루어진다.
- 책임
- 객체가 수행하는 업무나 기능
- 특정한 역할을 수행하며, 이를 위한 책임을 가지게 되는데 객체는 외부에 제공하는 서비스 또는 기능, 그리고 내부적으로 수행하는 작업에 관한 것이다.
- 객체의 행위와 상태를 결정짓는 중요한 개념으로, 객체는 자신의 책임을 수행하는 데 필요한 정보를 가지고 있어야 한다.
- 역할
- 유사한 책임을 가진 객체들의 그룹을 의미
- 객체가 특정한 역할을 맡으면, 해당 객체는 그 역할에 따른 책임을 수행할 수 있어야 한다.
- 동일한 역할을 수행하는 객체들은 서로 대체 가능하도록 설계할 수 있다.
- 객체지향 설계에서 역할은 코드의 재사용성과 유지보수성을 향상시키는 데 기여한다.
6. SOLID: 객체지향 설계 5가지 원칙
- 단일 책임 원칙(SRP, Single Responsibility Principle)
: 클래스가 하나의 책임이나 기능만을 담당- 기본적으로 작은 단위와 단일 기능을 가진 클래스를 설계 해야한다.
- 어떻게 단일 책임이 있는지 알 수 있을까?
- 클래스에 속성, 메서드가 많아서 가독성, 유지보수에 어려움을 느낄 때
- 많은 수의 클래스에 의존하는 경우
- 클래스의 이름을 비즈니스적으로 정확하게 명명할 수 없는 경우
- 응집도가 낮은 경우 = 메서드들이 클래스의 적은 수의 속성만 사용하는 경우
- 개방/폐쇄 원칙(OCP, Open/Closed Principle)
: 확장 => 개방, 수정 => 폐쇄- 새로운 기능을 추가할 때 기존의 모듈, 클래스, 함수를 수정하기보다는 기존 코드를 기반으로 모듈, 클래스, 함수 등을 추가하는 방식으로 코드를 확장 해야한다.
- 리스코프 치환 원칙(LSP, Liskov Substitution Principle)
: 상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야한다.- 계약에 따른 설계
- 상위 클래스에서 선언한 대로 기능이 동작
- 입력, 추력, 예외 모두 상위 클래스를 따른다.
- 상위 클래스의 특별 지침을 모두 준수
==> 다형성: 코드를 구현하는 방식
- 인터페이스 분리 원칙(ISP, Interface Segregation Principle)
: 클라이언트는 자신이 사용하는 메서드에만 의존해야한다.- 클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다.
- 하나의 일반적인 인터페이스 보다 여러 개의 구체적인 인터페이스가 낫다.
- 의존성 역전 원칙(DIP, Dependency Inversion Principle)
: 고수준 모듈은 저수준 모듈의 구현에 의존해서는 안 된다. 저수준 모듈이 고수준 모듈에서 정의한 추상 타입에 의존해야한다.- 고수준 모듈: 의미 있는 단일 기능을 제공
- 저수준 모듈: 고수준 모듈의 기능을 구현하기 위해 필요한 하위 기능의 실제 구현
'다양한 Dev. > 기본 정리' 카테고리의 다른 글
2024.03.12 - [Path Parameter] vs. [Query Parameter] (0) | 2024.03.12 |
---|---|
2024.02.16 - Github와 Discord 연동하기 (0) | 2024.02.16 |
2023.09.30 - Swagger(Nest.js) 작성 방법 (0) | 2023.09.30 |
2023.09.26 - Swagger(Nest.js) (0) | 2023.09.26 |
2023.07.29 - 쿠키와 세션의 차이점 (0) | 2023.07.29 |