일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- vscode
- erp
- 정규식
- MSSQL
- ping
- 핑
- 이메일주소
- 자바스크립트
- 포트
- 더존ERP
- 메일
- jquery
- codeigniter
- 트랜젝션
- MYSQL
- js
- 유효성
- 리눅스
- 정규표현식
- port
- pingtest
- crontab
- sp_who
- 핑테스트
- php
- 문자열
- JavaScript
- 목차만들기
- sendmail
- python
Archives
- Today
- Total
ioerror
SOLID 원칙 본문
반응형
OOP(객체지향프로그래밍)의 대표적인 원칙이다.
프로그래밍 뿐만 아니라 Database 설계시에도 적용된다.
S : Single Responsibility Principle - 단일 책임 원칙
원칙: 클래스는 하나의 책임만 가져야 하고, 변경되는 이유도 하나뿐이어야 함
나쁜 예시(PHP)
class UserManager {
public function createUser($data) {
// 사용자 생성 로직
$user = new User($data);
// 데이터베이스 저장
$this->saveToDatabase($user);
// 이메일 발송
$this->sendWelcomeEmail($user);
// 로그 기록
$this->logUserCreation($user);
return $user;
}
private function saveToDatabase($user) { /* DB 로직 */ }
private function sendWelcomeEmail($user) { /* 이메일 로직 */ }
private function logUserCreation($user) { /* 로그 로직 */ }
}
좋은 예시(PHP)
class User {
private $name;
private $email;
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
}
// getter, setter 등 사용자 관련 메서드만
}
class UserRepository {
public function save(User $user) {
// 데이터베이스 저장 로직만 담당
}
}
class EmailService {
public function sendWelcomeEmail(User $user) {
// 이메일 발송 로직만 담당
}
}
class Logger {
public function logUserCreation(User $user) {
// 로그 기록 로직만 담당
}
}
class UserService {
private $userRepository;
private $emailService;
private $logger;
public function __construct(UserRepository $repo, EmailService $email, Logger $logger) {
$this->userRepository = $repo;
$this->emailService = $email;
$this->logger = $logger;
}
public function createUser($data) {
$user = new User($data['name'], $data['email']);
$this->userRepository->save($user);
$this->emailService->sendWelcomeEmail($user);
$this->logger->logUserCreation($user);
return $user;
}
}
나쁜예시(JS)
class UserManager {
createUser(data) {
// 사용자 생성 로직
const user = new User(data);
// 데이터베이스 저장
this.saveToDatabase(user);
// 이메일 발송
this.sendWelcomeEmail(user);
// 로그 기록
this.logUserCreation(user);
return user;
}
saveToDatabase(user) { /* DB 로직 */ }
sendWelcomeEmail(user) { /* 이메일 로직 */ }
logUserCreation(user) { /* 로그 로직 */ }
}
좋은예시(JS)
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
}
class UserRepository {
save(user) {
// 데이터베이스 저장 로직만 담당
}
}
class EmailService {
sendWelcomeEmail(user) {
// 이메일 발송 로직만 담당
}
}
class Logger {
logUserCreation(user) {
// 로그 기록 로직만 담당
}
}
class UserService {
constructor(userRepository, emailService, logger) {
this.userRepository = userRepository;
this.emailService = emailService;
this.logger = logger;
}
createUser(data) {
const user = new User(data.name, data.email);
this.userRepository.save(user);
this.emailService.sendWelcomeEmail(user);
this.logger.logUserCreation(user);
return user;
}
}
O: Open / Cloded Principle - 개방/폐쇄 원칙
원칙: 클래스는 확장(상속)에는 열려있고, 수정에는 닫혀있어야 함
나쁜예시(PHP)
class AreaCalculator {
public function calculate($shapes) {
$area = 0;
foreach ($shapes as $shape) {
if ($shape->type === 'rectangle') {
$area += $shape->width * $shape->height;
} elseif ($shape->type === 'circle') {
$area += pi() * pow($shape->radius, 2);
}
// 새로운 도형을 추가할 때마다 이 메서드를 수정해야 함
}
return $area;
}
}
좋은예시
interface Shape {
public function area();
}
class Rectangle implements Shape {
private $width;
private $height;
public function __construct($width, $height) {
$this->width = $width;
$this->height = $height;
}
public function area() {
return $this->width * $this->height;
}
}
class Circle implements Shape {
private $radius;
public function __construct($radius) {
$this->radius = $radius;
}
public function area() {
return pi() * pow($this->radius, 2);
}
}
class Triangle implements Shape {
private $base;
private $height;
public function __construct($base, $height) {
$this->base = $base;
$this->height = $height;
}
public function area() {
return 0.5 * $this->base * $this->height;
}
}
class AreaCalculator {
public function calculate($shapes) {
$totalArea = 0;
foreach ($shapes as $shape) {
$totalArea += $shape->area(); // 새로운 도형이 추가되어도 수정 불필요
}
return $totalArea;
}
}
나쁜예시(JS)
class AreaCalculator {
calculate(shapes) {
let area = 0;
for (const shape of shapes) {
if (shape.type === 'rectangle') {
area += shape.width * shape.height;
} else if (shape.type === 'circle') {
area += Math.PI * Math.pow(shape.radius, 2);
}
// 새로운 도형을 추가할 때마다 이 메서드를 수정해야 함
}
return area;
}
}
좋은예시(JS)
class Shape {
area() {
throw new Error("area() method must be implemented");
}
}
class Rectangle extends Shape {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
area() {
return this.width * this.height;
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
area() {
return Math.PI * Math.pow(this.radius, 2);
}
}
class Triangle extends Shape {
constructor(base, height) {
super();
this.base = base;
this.height = height;
}
area() {
return 0.5 * this.base * this.height;
}
}
class AreaCalculator {
calculate(shapes) {
let totalArea = 0;
for (const shape of shapes) {
totalArea += shape.area(); // 새로운 도형이 추가되어도 수정 불필요
}
return totalArea;
}
}
L : Liskov Subsitituion Principle - 리스코프 치환 원칙
원칙: 자식 클래스는 부모 클래스를 완전히 대체할 수 있어야 함
나쁜예시(PHP)
class Bird {
public function fly() {
return "날고 있어요";
}
}
class Penguin extends Bird {
public function fly() {
throw new Exception("펭귄은 날 수 없어요!"); // 부모의 계약을 위반
}
}
function makeBirdFly(Bird $bird) {
return $bird->fly(); // Penguin 객체가 들어오면 예외 발생
}
좋은예시(PHP)
abstract class Bird {
abstract public function move();
}
interface Flyable {
public function fly();
}
class Sparrow extends Bird implements Flyable {
public function move() {
return "참새가 움직여요";
}
public function fly() {
return "참새가 날고 있어요";
}
}
class Penguin extends Bird {
public function move() {
return "펭귄이 걸어요";
}
public function swim() {
return "펭귄이 수영해요";
}
}
function makeBirdMove(Bird $bird) {
return $bird->move(); // 모든 새는 움직일 수 있음
}
function makeFlyableFly(Flyable $flyable) {
return $flyable->fly(); // 날 수 있는 것들만 날게 함
}
나쁜예시(JS)
class Bird {
fly() {
return "날고 있어요";
}
}
class Penguin extends Bird {
fly() {
throw new Error("펭귄은 날 수 없어요!"); // 부모의 계약을 위반
}
}
function makeBirdFly(bird) {
return bird.fly(); // Penguin 객체가 들어오면 예외 발생
}
좋은예시(JS)
class Bird {
move() {
throw new Error("move() method must be implemented");
}
}
class Sparrow extends Bird {
move() {
return "참새가 움직여요";
}
fly() {
return "참새가 날고 있어요";
}
}
class Penguin extends Bird {
move() {
return "펭귄이 걸어요";
}
swim() {
return "펭귄이 수영해요";
}
}
function makeBirdMove(bird) {
return bird.move(); // 모든 새는 움직일 수 있음
}
function makeFlyableFly(flyable) {
if (typeof flyable.fly === 'function') {
return flyable.fly(); // 날 수 있는 것들만 날게 함
}
throw new Error("This object cannot fly");
}
I : Interface Segergation Principle - 인터페이스 분리 원칙
원칙: 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 됨
나쁜예시(PHP)
interface Worker {
public function work();
public function eat();
public function sleep();
}
class HumanWorker implements Worker {
public function work() {
return "인간이 일해요";
}
public function eat() {
return "인간이 먹어요";
}
public function sleep() {
return "인간이 자요";
}
}
class RobotWorker implements Worker {
public function work() {
return "로봇이 일해요";
}
public function eat() {
// 로봇은 먹지 않는데 구현해야 함
throw new Exception("로봇은 먹지 않아요");
}
public function sleep() {
// 로봇은 자지 않는데 구현해야 함
throw new Exception("로봇은 자지 않아요");
}
}
좋은예시(PHP)
interface Workable {
public function work();
}
interface Eatable {
public function eat();
}
interface Sleepable {
public function sleep();
}
class HumanWorker implements Workable, Eatable, Sleepable {
public function work() {
return "인간이 일해요";
}
public function eat() {
return "인간이 먹어요";
}
public function sleep() {
return "인간이 자요";
}
}
class RobotWorker implements Workable {
public function work() {
return "로봇이 일해요";
}
// 로봇은 필요한 인터페이스만 구현
}
나쁜예시(JS)
class Worker {
work() { throw new Error("Must implement work()"); }
eat() { throw new Error("Must implement eat()"); }
sleep() { throw new Error("Must implement sleep()"); }
}
class HumanWorker extends Worker {
work() { return "인간이 일해요"; }
eat() { return "인간이 먹어요"; }
sleep() { return "인간이 자요"; }
}
class RobotWorker extends Worker {
work() { return "로봇이 일해요"; }
eat() {
// 로봇은 먹지 않는데 구현해야 함
throw new Error("로봇은 먹지 않아요");
}
sleep() {
// 로봇은 자지 않는데 구현해야 함
throw new Error("로봇은 자지 않아요");
}
}
좋은예시(JS)
// JavaScript는 인터페이스가 없으므로 믹스인 패턴 사용
const Workable = {
work() { throw new Error("Must implement work()"); }
};
const Eatable = {
eat() { throw new Error("Must implement eat()"); }
};
const Sleepable = {
sleep() { throw new Error("Must implement sleep()"); }
};
class HumanWorker {
work() { return "인간이 일해요"; }
eat() { return "인간이 먹어요"; }
sleep() { return "인간이 자요"; }
}
class RobotWorker {
work() { return "로봇이 일해요"; }
// 로봇은 work만 구현하면 됨
}
// 믹스인 적용
Object.assign(HumanWorker.prototype, Workable, Eatable, Sleepable);
Object.assign(RobotWorker.prototype, Workable);
D: Dependency Inversion Principle - 의존성 역전 원칙
원칙: 고수준 모듈은 저수준 모듈에 의존하면 안 되고, 둘 다 추상화에 의존해야 함
나쁜예시(PHP)
class MySQLDatabase {
public function save($data) {
// MySQL 특정 저장 로직
echo "MySQL에 저장: " . $data;
}
}
class UserService {
private $database;
public function __construct() {
$this->database = new MySQLDatabase(); // 구체적인 클래스에 직접 의존
}
public function saveUser($userData) {
$this->database->save($userData);
}
}
좋은예시(PHP)
interface DatabaseInterface {
public function save($data);
}
class MySQLDatabase implements DatabaseInterface {
public function save($data) {
echo "MySQL에 저장: " . $data;
}
}
class PostgreSQLDatabase implements DatabaseInterface {
public function save($data) {
echo "PostgreSQL에 저장: " . $data;
}
}
class UserService {
private $database;
public function __construct(DatabaseInterface $database) {
$this->database = $database; // 추상화에 의존
}
public function saveUser($userData) {
$this->database->save($userData);
}
}
// 사용 예시
$mysqlDB = new MySQLDatabase();
$userService = new UserService($mysqlDB); // 의존성 주입
$postgresDB = new PostgreSQLDatabase();
$userService2 = new UserService($postgresDB); // 쉽게 교체 가능
나쁜예시(JS)
class MySQLDatabase {
save(data) {
// MySQL 특정 저장 로직
console.log("MySQL에 저장:", data);
}
}
class UserService {
constructor() {
this.database = new MySQLDatabase(); // 구체적인 클래스에 직접 의존
}
saveUser(userData) {
this.database.save(userData);
}
}
좋은예시(JS)
class DatabaseInterface {
save(data) {
throw new Error("save() method must be implemented");
}
}
class MySQLDatabase extends DatabaseInterface {
save(data) {
console.log("MySQL에 저장:", data);
}
}
class PostgreSQLDatabase extends DatabaseInterface {
save(data) {
console.log("PostgreSQL에 저장:", data);
}
}
class UserService {
constructor(database) {
this.database = database; // 추상화에 의존
}
saveUser(userData) {
this.database.save(userData);
}
}
// 사용 예시
const mysqlDB = new MySQLDatabase();
const userService = new UserService(mysqlDB); // 의존성 주입
const postgresDB = new PostgreSQLDatabase();
const userService2 = new UserService(postgresDB); // 쉽게 교체 가능
반응형
'개발이야기' 카테고리의 다른 글
VSCode 자주 사용하는 단축키 - 커서 이동, 선택, 복사 (0) | 2021.05.22 |
---|---|
자주 사용하는 VSCODE 확장 플러그인 (0) | 2021.05.19 |
Coding Convention (0) | 2020.04.29 |
[공통] 코딩컨벤션 (Coding Convention) (0) | 2019.12.01 |
코딩컨벤션 (Coding Convention) (0) | 2019.11.22 |
Comments