ðžSOLIDååïŒã³ãŒãããããã«ããŠç解ããããããðž
ã³ãŒãã£ã³ã°ããããšããèªã¿ãããç解ãããããã®ã«ããããšã倧åã§ããç¹ã«å€§ããªãããžã§ã¯ãã§è€æ°ã®éšåããããšãã¯ãç¹ã«éèŠã§ããã³ãŒããç解ããããããããã®æ¹æ³ãšããŠãSOLIDã®ååãå®ãããšããããŸãã
SOLIDãšã¯ã5ã€ã®ååãæå³ããŸãïŒ
- Single Responsibility Principle (SRP)
- Open-Closed Principle (OCP)
- Liskov Substitution Principle (LSP)
- Interface Segregation Principle (ISP)
- Dependency Inversion Principle (DIP)
ããããã®ååãäžã€ãã€èŠãŠãããã©ã®ããã«ã³ãŒããããè¯ãããã®ããèŠãŠãããŸãããã
説æ
Single Responsibility Principle (SRP)
SRPã¯ãã³ãŒãã®åéšåã1ã€ã®ä»äºã ããããããã«ãããšããããšãèšã£ãŠããŸããããã¯ã1ã€ã®ã³ãŒããè€æ°ã®ããšãããå Žåãå°ããªéšåã«åããŠãããããã«ä»äºãäžããã¹ãã ãšããããšãæå³ããŸãã
äŸãã°ããã¿ã³ãäœåã¯ãªãã¯ãããããèšé²ããç»é¢ã«ã¯ãªãã¯æ°ã衚瀺ããã³ãŒãããããšããŸããSRPã«ãããšããã®ã³ãŒãã¯2ã€ã®å¥ã ã®éšåã«åããã¹ãã§ãïŒ1ã€ã¯ã¯ãªãã¯ãèšé²ãããã®ããã1ã€ã¯ç»é¢ã«ã¯ãªãã¯æ°ã衚瀺ãããã®ã§ãã
// Incorrect way of doing it
let clickCount = 0;
function handleButtonClick() {
clickCount += 1;
document.getElementById("click-count").innerHTML = clickCount;
}
// Correct way of doing it
let clickCount = 0;
function handleButtonClick() {
clickCount += 1;
}
function updateClickCount() {
document.getElementById("click-count").innerHTML = clickCount;
}
Open-Closed Principle (OCP)
OCPãšã¯ãã³ãŒããæ¡åŒµããããã«éããŠããã¹ãã§ãããä¿®æ£ããããã«ã¯éããŠããã¹ãã ãšããããšã§ããããã¯ãæ¢åã®ã³ãŒããå€æŽããã«æ°ããæ©èœãè¿œå ã§ããããã«ããããšãæå³ããŸãã
äŸãã°ã2ã€ã®æ°åã足ãåãããããã°ã©ã ããããšããŸããOCPã«åŸããšãæ¢åã®è¶³ãç®ã®ã³ãŒããå€æŽããã«ãæ°ããæ©èœïŒåŒãç®ãªã©ïŒãè¿œå ã§ããããã«ãªããŸãã
// Incorrect way of doing it
function add(a, b) {
return a + b;
}
// It is not a good idea to change the `add` function to a `subtract` function.
function subtract(a, b) {
return a - b;
}
// Correct way of doing it
class Calculator {
static add(a, b) {
return a + b;
}
static subtract(a, b) {
return a - b;
}
}
Liskov Substitution Principle (LSP)
LSPãšã¯ã芪ã¯ã©ã¹ã䜿ãå Žæã§ãµãã¯ã©ã¹ã䜿ããããã«ãããšãããã®ã§ããããã¯ããµãã¯ã©ã¹ã芪ã¯ã©ã¹ã®ãããè¯ããããŒãžã§ã³ã§ããã芪ã¯ã©ã¹ã䜿ãã³ãŒããå£ããªãããšãæå³ããŸãã
äŸãã°ããAnimalããšãã芪ã¯ã©ã¹ãšãDogsããšãããµãã¯ã©ã¹ããããšããŸããLSPã«ãããšããAnimalããªããžã§ã¯ãã䜿ãå Žæã§ãDogããªããžã§ã¯ãã䜿ããã¯ãã§ãããã³ãŒãã¯æ£ããåäœããã¯ãã§ãã
class Animals {
constructor(name) {
this.name = name;
}
speak() {
return "Animals make noise";
}
}
class Dogs extends Animals {
speak() {
return "Woof";
}
}
const animal = new Animals("Animals");
console.log(animal.speak()); // prints "Animals make noise"
const dog = new Dogs("Dog");
console.log(dog.speak()); // prints "Woof"
console.log(dog instanceof Animals); // prints "true"
Interface Segregation Principle (ISP)
ISPãšã¯ãã¯ã©ã€ã¢ã³ãã«äœ¿ããªãã€ã³ã¿ãŒãã§ãŒã¹ã匷å¶ããã¹ãã§ã¯ãªããšããããšã§ããããã¯ãé¢é£ããæ©èœã®ã°ã«ãŒãããšã«å°ããªã€ã³ã¿ãŒãã§ãŒã¹ãäœæããã¹ãã ãšããããšã§ãã
äŸãã°ããAutomobileããšããã€ã³ã¿ãŒãã§ãŒã¹ããèµ°è¡ãšé£è¡ã®äž¡æ¹ã®æ©èœãæã£ãŠãããšããŸããISPã«ãããšãèµ°è¡ããã§ããªãè»ã®ã¯ã©ã¹ã¯ããAutomobileãã€ã³ã¿ãŒãã§ãŒã¹ããã®é£è¡æ©èœã匷å¶ãããŠã¯ãªããŸããã
// Incorrect way of doing it
interface Automobile {
drive(): void;
fly(): void;
}
class Car implements Automobile {
drive(): void {
// code for driving
}
fly(): void {
// code for flying (not applicable for cars)
}
}
// Correct way of doing it
interface Drivable {
drive(): void;
}
interface Flyable {
fly(): void;
}
class Car implements Drivable {
drive(): void {
// code for driving
}
}
Dependency Inversion Principle (DIP)
DIPãšã¯ãé«ã¬ãã«ã®ã¢ãžã¥ãŒã«ã¯äœã¬ãã«ã®ã¢ãžã¥ãŒã«ã«äŸåããŠã¯ãããªãããäž¡æ¹ãšãæœè±¡åã«äŸåããã¹ãã ãšããããšã§ããããã¯ãããªãã®ã³ãŒããç¹å®ã®ã¯ã©ã¹ãé¢æ°ã«äŸåããŠã¯ãããªããšããããšãæå³ããŸããããããæœè±¡çãªæŠå¿µã«äŸåããã¹ãã§ãã
äŸãã°ããCarããšããã¯ã©ã¹ããEngineããšããã¯ã©ã¹ã«äŸåããŠãããšããŸããDIPã«ãããšããCarãã¯ã©ã¹ã¯ç¹å®ã®ãEngineãã¯ã©ã¹ã«äŸåããŠã¯ãããããšã³ãžã³ãšã¯äœããšããæœè±¡åã«äŸåãã¹ãã§ãã
// Incorrect way of doing it
class Engine {
start(): void {
// code for starting the engine
}
}
class Car {
private engine: Engine;
constructor() {
this.engine = new Engine();
}
start(): void {
this.engine.start();
}
}
// Correct way of doing it
interface Engine {
start(): void;
}
class RealEngine implements Engine {
start(): void {
// code for starting the engine
}
}
class Car {
private engine: Engine;
constructor(engine: Engine) {
this.engine = engine;
}
start(): void {
this.engine.start();
}
}
const car = new Car(new RealEngine());
ã±ãŒã¹ã䜿ã
1. ã€ã³ã¿ãŒãããã·ã§ããã³ã°ã®ãã§ãã¯ã¢ãŠãããã»ã¹
ã客æ§ãã«ãŒãã«ã¢ã€ãã ãè¿œå ãããã§ãã¯ã¢ãŠãããŒãžã«é²ãeã³ããŒã¹ãŠã§ããµã€ããèããŸãããããã§ãã¯ã¢ãŠãããã»ã¹ã«ã¯ãã¢ã€ãã ã®åèšéé¡ãèšç®ããå²åŒãããã¢ãŒã·ã§ã³ãé©çšãããããŠæ¯æããåŠçããããšãå«ãŸããŸãã
SRPã«ãããšããã§ãã¯ã¢ãŠãããã»ã¹ãç°ãªãã¯ã©ã¹ã«åå²ãã¹ãã§ãããããããç¬èªã®è²¬ä»»ãæã€ããã«ããŸããäŸãã°ãã«ãŒãå
ã®ã¢ã€ãã ã远跡ããCart
ã¯ã©ã¹ãå²åŒãããã¢ãŒã·ã§ã³ãé©çšããDiscounts
ã¯ã©ã¹ããããŠæ¯æãåŠçãè¡ãPayment
ã¯ã©ã¹ãªã©ãäœæããããšãã§ããŸãã
class Cart {
items = [];
addItem(item) {
this.items.push(item);
}
getTotal() {
return this.items.reduce((total, item) => total + item.price, 0);
}
}
class Discounts {
applyDiscount(total) {
return total * 0.9; // 10% off
}
}
class Payment {
processPayment(total) {
// code for processing the payment
}
}
class Checkout {
cart;
discounts;
payment;
constructor(cart, discounts, payment) {
this.cart = cart;
this.discounts = discounts;
this.payment = payment;
}
processCheckout() {
const total = this.discounts.applyDiscount(this.cart.getTotal());
this.payment.processPayment(total);
}
}
const cart = new Cart();
cart.addItem({ name: "item1", price: 20 });
cart.addItem({ name: "item2", price: 30 });
const checkout = new Checkout(cart, new Discounts(), new Payment());
checkout.processCheckout();
ãã®äŸã§ã¯ãããããã®ã¯ã©ã¹ã«1ã€ã®è²¬ä»»ããããŸãïŒCart
ã¯ã©ã¹ã¯ã«ãŒãå
ã®ååã远跡ããŸããDiscounts
ã¯ã©ã¹ã¯å²åŒãé©çšããŸããPayment
ã¯ã©ã¹ã¯æ¯æããåŠçããŸããCheckout
ã¯ã©ã¹ã¯ããã»ã¹ã調æŽããŸããããã«ãããã³ãŒããä¿å®æ§ãé«ããç解ãããããªããŸãã
2. 倩æ°ã¢ããª
倩æ°ã¢ããªã«ãçŸåšã®å Žæã®æ°æž©ã湿床ãæ°å§ã衚瀺ããæ©èœããããŸããæ°ããæ©èœãšããŠã颚éãšé¢šåãã衚瀺ããæ©èœãè¿œå ããããšæããŸããOpen-Closed PrincipleïŒOCPïŒã«ãããšãæ¢åã®ã³ãŒããå€æŽããããšãªãããã®æ°ããæ©èœãè¿œå ã§ããã¯ãã§ãã
class WeatherData {
constructor(temperature, humidity, pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
}
}
class WeatherDisplay {
display(weatherData) {
console.log(`Temperature: ${weatherData.temperature}`);
console.log(`Humidity: ${weatherData.humidity}`);
console.log(`Pressure: ${weatherData.pressure}`);
}
}
class WindDisplay {
display(weatherData) {
console.log(`Wind speed: ${weatherData.windSpeed}`);
console.log(`Wind direction: ${(weatherData, windDirection)}`);
}
}
class WeatherApp {
weatherData;
weatherDisplay;
windDisplay;
constructor(weatherData) {
this.weatherData = weatherData;
this.weatherDisplay = new WeatherDisplay();
this.windDisplay = new WindDisplay();
}
displayWeather() {
this.weatherDisplay.display(this.weatherData);
this.windDisplay.display(this.weatherData);
}
}
const weatherData = new WeatherData(72, 50, 1013);
weatherData.windSpeed = 5;
weatherData.windDirection = "NW";
const weatherApp = new WeatherApp(weatherData);
weatherApp.displayWeather();
ãã®äŸã§ã¯ãæ¢åã®WeatherDisplay
ã¯ã©ã¹ãå€æŽããã«ãæ°ããWindDisplay
ã¯ã©ã¹ãè¿œå ããããšã§ãWeatherApp
ã¯ã©ã¹ãæ¡åŒµã§ããŸããããã«ãããæ¢åã®ã³ãŒãã«åœ±é¿ãäžããããšãªããã¢ããªã«æ°ããæ©èœãè¿œå ã§ããŸãã
3. ã²ãŒã ã®ãã£ã©ã¯ã¿ãŒ
æ°ãããã£ã©ã¯ã¿ãŒãè¿œå ããããšæã£ãŠããŸãããæ¢åã®ã²ãŒã ã¡ã«ãã¯ã¹ãå£ããªãããã«ãããã§ããLSPãšãããã®ã䜿ãã°ã芪ãã£ã©ã¯ã¿ãŒã¯ã©ã¹ã䜿ãå Žæã§æ°ãããã£ã©ã¯ã¿ãŒã¯ã©ã¹ã䜿ã£ãŠããã²ãŒã ãæ£ããåãããã«ãªããŸãã
class Character {
move() {
console.log("Character moved");
}
}
class Warrior extends Character {
attack() {
console.log("Warrior attacked");
}
}
class Mage extends Character {
castSpell() {
console.log("Mage cast a spell");
}
}
class Paladin extends Warrior {
heal() {
console.log("Paladin healed");
}
}
const characters = [new Warrior(), new Mage(), new Paladin()];
for (let character of characters) {
character.move();
if (character instanceof Warrior) {
character.attack();
}
if (character instanceof Mage) {
character.castSpell();
}
if (character instanceof Paladin) {
character.heal();
}
}
ãã®äŸã§ã¯ãPaladin
ã¯ã©ã¹ã¯Warrior
ã¯ã©ã¹ã®ãµãã¯ã©ã¹ã§ãå埩ããç¬èªã®èœåãæã£ãŠããŸããã芪ã¯ã©ã¹ããmove
ã¡ãœãããæ£ããå®è£
ããŠããã®ã§ããã£ã©ã¯ã¿ãŒãªããžã§ã¯ãã䜿ãããã©ãã§ã䜿ããŸããããã«ãããæ¢åã®ã²ãŒã ã¡ã«ãã¯ã¹ãå£ããã«æ°ãããã£ã©ã¯ã¿ãŒã¿ã€ããè¿œå ã§ããŸãã
4. ãã£ããã¢ããª
ã¡ãã»ãŒãžãéä¿¡ããæ©èœãšãã¡ã€ã«ãéä¿¡ããæ©èœãåããããšã§ãçæ¹ã®æ©èœããå¿ èŠãªãã¯ã©ã€ã¢ã³ãã¯ããçæ¹ã®æ©èœãå®è£ ããå¿ èŠããªããªããŸããã€ã³ã¿ãŒãã§ãŒã¹åé¢ã®ååïŒISPïŒã«åŸããšãã¡ãã»ãŒãžãéä¿¡ããã€ã³ã¿ãŒãã§ãŒã¹ãšãã¡ã€ã«ãéä¿¡ããã€ã³ã¿ãŒãã§ãŒã¹ãåããã¹ãã§ãã
interface MessageSender {
sendMessage(message: string): void;
}
interface FileSender {
sendFile(file: File): void;
}
class ChatClient implements MessageSender {
sendMessage(message: string): void {
// code for sending a message
}
}
class FileTransferClient implements FileSender {
sendFile(file: File): void {
// code for sending a file
}
}
class AdvancedChatClient implements MessageSender, FileSender {
sendMessage(message: string): void {
// code for sending a message
}
sendFile(file: File): void {
// code for sending a file
}
}
const chatClient = new ChatClient();
chatClient.sendMessage("Hello!");
const fileTransferClient = new FileTransferClient();
fileTransferClient.sendFile(new File("file.txt"));
const advancedChatClient = new AdvancedChatClient();
advancedChatClient.sendMessage("Hello!");
advancedChatClient.sendFile(new File("file.txt"));
ãã®äŸã§ã¯ãChatClient
ã¯ã©ã¹ã¯MessageSender
ã€ã³ã¿ãŒãã§ãŒã¹ã®ã¿ãå®è£
ããFileSender
ã€ã³ã¿ãŒãã§ãŒã¹ãå®è£
ããå¿
èŠã¯ãããŸããããŸããFileTransferClient
ã¯ã©ã¹ã¯FileSender
ã€ã³ã¿ãŒãã§ãŒã¹ã®ã¿ãå®è£
ããMessageSender
ã€ã³ã¿ãŒãã§ãŒã¹ãå®è£
ããå¿
èŠã¯ãããŸãããããã«ãããã¯ã©ã€ã¢ã³ãã¯å¿
èŠãªæ©èœã ããå®è£
ããã³ãŒããæ確ãã€ç解ããããä¿ã€ããšãã§ããŸãã
5. ãœãŒã·ã£ã«ã¡ãã£ã¢ãã©ãããã©ãŒã
ç§ãã¡ãããã¹ããç»åã®æŽæ°ãæ±ãã³ãŒããå€æŽããã«ããŠãŒã¶ãŒãåç»ã®æŽæ°ãæçš¿ã§ããæ°ããæ©èœãè¿œå ããããšããŸããäŸåæ§å転ååïŒDIPïŒã«ãããšãæŽæ°ãåŠçããã³ãŒãã¯ç¹å®ã®ã¯ã©ã¹ãé¢æ°ã«äŸåããªãããã«ããŠãæœè±¡çãªæŠå¿µã«äŸåããããã«ããªããã°ãªããŸããã
interface Update {
display(): void;
}
class TextUpdate implements Update {
text: string;
constructor(text: string) {
this.text = text;
}
display(): void {
console.log(`Text Update: ${this.text}`);
}
}
class ImageUpdate implements Update {
imageUrl: string;
constructor(imageUrl: string) {
this.imageUrl = imageUrl;
}
display(): void {
console.log(Image Update: ${ this.imageUrl });
}
}
class VideoUpdate implements Update {
videoUrl: string;
constructor(videoUrl: string) {
this.videoUrl = videoUrl;
}
display(): void {
console.log(Video Update: ${ this.videoUrl });
}
}
class SocialMediaApp {
updates: Update[];
constructor() {
this.updates = [];
}
addUpdate(update: Update) {
this.updates.push(update);
}
displayUpdates() {
this.updates.forEach(update => update.display());
}
}
const socialMediaApp = new SocialMediaApp();
socialMediaApp.addUpdate(new TextUpdate("Hello, world!"));
socialMediaApp.addUpdate(new ImageUpdate("image.jpg"));
socialMediaApp.addUpdate(new VideoUpdate("video.mp4"));
socialMediaApp.displayUpdates();
ãã®äŸã§ã¯ãããã¹ããç»åããŸãã¯ãããªæŽæ°ãåŠçããç¹å®ã®ã¯ã©ã¹ã§ã¯ãªãããUpdateãã€ã³ã¿ãŒãã§ãŒã¹ã®æœè±¡çãªæŠå¿µã«äŸåããŠããŸããããã«ããããããªæŽæ°ãªã©ã®æ°ããã¿ã€ãã®æŽæ°ãããã¹ããšç»åã®æŽæ°ãåŠçããæ¢åã®ã³ãŒããå€æŽããããšãªãè¿œå ã§ããŸãã
Conclusion
SOLIDã®ååã¯ãéçºè ããããã§ä¿å®æ§ããããç解ããããã³ãŒããæžãã®ãå©ããã¬ã€ãã©ã€ã³ã®éãŸãã§ãããããã®ååãå®ãããšã§ãéçºè ã¯ã³ãŒããæ±ãããããå°æ¥çã«æ¡åŒµãããå€æŽãããããã®ã容æã«ãªããŸãããœãªããã®ååãšã¯ãSingle Responsibility Principle ïŒSRPïŒ, Open-Closed PrincipleïŒOCPïŒ, Liskov Substitution Principle ïŒLSPïŒ, Interface Segregation PrincipleïŒISPïŒ, and Dependency Inversion PrincipleïŒDIPïŒã®5ã€ã§ããããããã®ååã«ã¯ã解説ãå©ç¹ãå®éã®ã³ãŒãã¹ãããããå«ãå®çšäŸããããŸããéèŠãªã®ã¯ããœãªããã®ååã¯å³æ Œãªã«ãŒã«ã§ã¯ãªããç¹å®ã®ãããžã§ã¯ããã¢ããªã±ãŒã·ã§ã³ã«å¿ããŠç°ãªãæ¹æ³ã§é©çšã§ããäžè¬çãªã¬ã€ãã©ã€ã³ã§ãããšããããšã§ãã
Mình hy vá»ng bạn thÃch bà i viết nà y và há»c thêm Äược Äiá»u gì Äó má»i.
Donate mình má»t ly cafe hoặc 1 cây bút bi Äá» mình có thêm Äá»ng lá»±c cho ra nhiá»u bà i viết hay và chất lượng hÆ¡n trong tÆ°Æ¡ng lai nhé. à mà nếu bạn có bất kỳ câu há»i nà o thì Äừng ngại comment hoặc liên há» mình qua: Zalo - 0374226770 hoặc Facebook. Mình xin cảm Æ¡n.
Momo: NGUYá»N ANH TUẀN - 0374226770
TPBank: NGUYá»N ANH TUẀN - 0374226770 (hoặc 01681423001)
All rights reserved