> 웹 프론트엔드 > JS 튜토리얼 > JavaScript의 견고한 원칙

JavaScript의 견고한 원칙

Linda Hamilton
풀어 주다: 2024-12-28 19:34:11
원래의
322명이 탐색했습니다.

OOP 패러다임의 도입으로 상속, 다형성, 추상화, 캡슐화와 같은 주요 프로그래밍 개념이 대중화되었습니다. OOP는 Java, C, C#, JavaScript 등과 같은 여러 언어로 구현되어 널리 수용되는 프로그래밍 패러다임이 되었습니다. OOP 시스템은 시간이 지남에 따라 더욱 복잡해졌지만 소프트웨어는 여전히 변화에 저항했습니다. 소프트웨어 확장성을 향상시키고 코드 강성을 줄이기 위해 Robert C. Martin(일명 Bob 삼촌)은 2000년대 초반에 SOLID 원칙을 도입했습니다.

SOLID는 소프트웨어 엔지니어가 유지 관리 가능하고 확장 가능하며 유연하게 설계하고 작성하는 데 도움이 되는 단일 책임 원칙, 개방형 폐쇄 원칙, Liskov 대체 원칙, 인터페이스 분리 원칙, 종속성 반전 원칙 등 일련의 원칙으로 구성된 약어입니다. 암호. 그 목표는 무엇입니까? 객체 지향 프로그래밍(OOP) 패러다임에 따라 개발된 소프트웨어의 품질을 향상시킵니다.

이 기사에서는 SOLID의 모든 원칙을 자세히 살펴보고 가장 인기 있는 웹 프로그래밍 언어 중 하나인 JavaScript를 사용하여 구현되는 방법을 설명합니다.

단일 책임 원칙(SRP)

SOLID의 첫 글자는 단일 책임 원칙을 나타냅니다. 이 원칙은 클래스나 모듈이 하나의 역할만 수행해야 함을 시사합니다.

간단히 말하면, 클래스에는 단일한 책임이나 변경 이유가 있어야 합니다. 클래스가 둘 이상의 기능을 처리하는 경우 다른 기능에 영향을 주지 않고 한 기능을 업데이트하는 것은 까다롭습니다. 후속 문제로 인해 소프트웨어 성능이 저하될 수 있습니다. 이러한 종류의 문제를 피하려면 우려 사항이 분리된 모듈식 소프트웨어를 작성하는 데 최선을 다해야 합니다.

클래스에 책임이나 기능이 너무 많으면 수정하기가 어렵습니다. 단일 책임 원칙을 사용하면 모듈식이고 유지 관리가 더 쉽고 오류가 발생할 가능성이 적은 코드를 작성할 수 있습니다. 사람 모델을 예로 들어보겠습니다.

class Person {
    constructor(name, age, height, country){
      this.name = name
      this.age = age
      this.height = height
      this.country = country
  }
  getPersonCountry(){
    console.log(this.country)    
  }
  greetPerson(){
    console.log("Hi " + this.name)
  }
  static calculateAge(dob) { 
    const today = new Date(); 
    const birthDate = new Date(dob);

    let age = today.getFullYear() - birthDate.getFullYear(); 
    const monthDiff = today.getMonth() - birthDate.getMonth();

    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { 
      age--; 
    }
    return age; 
  } 
}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

위 코드는 괜찮은 것 같죠? 좀 빠지는. 샘플 코드는 단일 책임 원칙을 위반합니다. Person 클래스는 Person의 다른 인스턴스를 생성할 수 있는 유일한 모델이 아닌,calculateAge, GreetingPerson 및 getPersonCountry와 같은 다른 책임도 가집니다.

Person 클래스에서 처리하는 이러한 추가 책임으로 인해 코드의 한 측면만 변경하기가 어렵습니다. 예를 들어,calculateAge를 리팩터링하려고 시도한 경우 Person 모델도 리팩터링해야 할 수도 있습니다. 코드 베이스가 얼마나 작고 복잡한지에 따라 오류 없이 코드를 재구성하는 것이 어려울 수 있습니다.

잘못된 부분을 수정해 보겠습니다. 다음과 같이 책임을 여러 클래스로 나눌 수 있습니다.

class Person {
    constructor(name, age, height, country){
      this.name = name
      this.age = age
      this.height = height
      this.country = country
  }
  getPersonCountry(){
    console.log(this.country)    
  }
  greetPerson(){
    console.log("Hi " + this.name)
  }
  static calculateAge(dob) { 
    const today = new Date(); 
    const birthDate = new Date(dob);

    let age = today.getFullYear() - birthDate.getFullYear(); 
    const monthDiff = today.getMonth() - birthDate.getMonth();

    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { 
      age--; 
    }
    return age; 
  } 
}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

위의 샘플 코드에서 볼 수 있듯이 책임을 분리했습니다. Person 클래스는 이제 새로운 사람 객체를 생성할 수 있는 모델입니다. 그리고 PersonUtils 클래스에는 사람의 나이를 계산하는 단 하나의 책임이 있습니다. PersonService 클래스는 인사말을 처리하고 각 사람의 국가를 보여줍니다.

원한다면 이 프로세스를 더 줄일 수도 있습니다. SRP에 따라 우리는 클래스의 책임을 최소한으로 분리하여 문제가 있을 때 큰 번거로움 없이 리팩토링과 디버깅을 수행할 수 있기를 원합니다.

기능을 별도의 클래스로 나누어 단일 책임 원칙을 고수하고 각 클래스가 애플리케이션의 특정 측면을 담당하도록 보장합니다.

다음 원칙으로 넘어가기 전에 SRP를 준수한다고 해서 각 클래스가 엄격하게 단일 메소드나 기능을 포함해야 한다는 의미는 아닙니다.

그러나 단일 책임 원칙을 고수한다는 것은 클래스에 기능을 의도적으로 할당해야 함을 의미합니다. 수업이 수행하는 모든 것은 모든 면에서 밀접하게 연관되어 있어야 합니다. 여러 클래스가 여기저기 흩어져 있지 않도록 주의해야 하며, 코드 베이스에서 클래스가 너무 커지는 것을 반드시 피해야 합니다.

개방-폐쇄 원칙(OCP)

개방-폐쇄 원칙은 소프트웨어 구성 요소(클래스, 함수, 모듈 등)가 확장에는 개방되고 수정에는 폐쇄되어야 함을 명시합니다. 나는 당신이 무슨 생각을 하는지 알고 있습니다. 예, 이 아이디어는 처음에는 모순적으로 보일 수도 있습니다. 그러나 OCP는 소스 코드를 반드시 수정하지 않고도 확장이 가능하도록 소프트웨어를 설계하라고 요구하고 있습니다.

OCP는 대규모 코드 기반을 유지하는 데 매우 중요합니다. 이 지침을 통해 코드가 손상될 위험이 거의 또는 전혀 없이 새로운 기능을 도입할 수 있기 때문입니다. 새로운 요구 사항이 발생할 때 기존 클래스나 모듈을 수정하는 대신 새 구성 요소를 추가하여 관련 클래스를 확장해야 합니다. 이 작업을 수행하면서 새 구성 요소로 인해 시스템에 버그가 발생하지 않는지 확인하세요.

ES6 클래스 상속 기능을 사용하면 JavaScript에서 OC 원칙을 구현할 수 있습니다.

다음 코드 조각은 앞서 언급한 ES6 클래스 키워드를 사용하여 JavaScript에서 개방형-폐쇄형 원칙을 구현하는 방법을 보여줍니다.

class Person {
    constructor(name, dateOfBirth, height, country){
      this.name = name
      this.dateOfBirth = dateOfBirth
      this.height = height
      this.country = country
  }
}

class PersonUtils {
  static calculateAge(dob) { 
    const today = new Date(); 
    const birthDate = new Date(dob);

    let age = today.getFullYear() - birthDate.getFullYear(); 
    const monthDiff = today.getMonth() - birthDate.getMonth();

    if(monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { 
      age--; 
    }
    return age; 
  } 
}

const person = new Person("John", new Date(1994, 11, 23), "6ft", "USA"); 
console.log("Age: " + PersonUtils.calculateAge(person.dateOfBirth));

class PersonService {
  getPersonCountry(){
    console.log(this.country)    
  }
  greetPerson(){
    console.log("Hi " + this.name)
  }
}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

위 코드는 잘 작동하지만 직사각형의 면적만 계산하는 것으로 제한됩니다. 이제 계산해야 할 새로운 요구 사항이 있다고 상상해 보십시오. 예를 들어 원의 면적을 계산해야 한다고 가정해 보겠습니다. 이를 충족하려면 ShapeProcessor 클래스를 수정해야 합니다. 그러나 JavaScript ES6 표준에 따라 ShapeProcessor 클래스를 반드시 수정하지 않고도 새로운 모양의 영역을 설명하도록 이 기능을 확장할 수 있습니다.

다음과 같이 할 수 있습니다.

class Person {
    constructor(name, age, height, country){
      this.name = name
      this.age = age
      this.height = height
      this.country = country
  }
  getPersonCountry(){
    console.log(this.country)    
  }
  greetPerson(){
    console.log("Hi " + this.name)
  }
  static calculateAge(dob) { 
    const today = new Date(); 
    const birthDate = new Date(dob);

    let age = today.getFullYear() - birthDate.getFullYear(); 
    const monthDiff = today.getMonth() - birthDate.getMonth();

    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { 
      age--; 
    }
    return age; 
  } 
}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

위 코드 조각에서는 확장 키워드를 사용하여 Shape 클래스의 기능을 확장했습니다. 각 하위 클래스에서는 Area() 메서드의 구현을 재정의합니다. 이 원칙에 따라 ShapeProcessor 클래스의 기능을 수정하지 않고도 더 많은 모양과 프로세스 영역을 추가할 수 있습니다.

OCP가 중요한 이유는 무엇입니까?

  • 버그 감소: OCP는 시스템 수정을 방지하여 대규모 코드 기반의 버그를 방지하는 데 도움이 됩니다.
  • 소프트웨어 적응성 장려: OCP는 또한 소스 코드를 손상시키거나 변경하지 않고도 소프트웨어에 새로운 기능을 추가할 수 있는 용이성을 향상시킵니다.
  • 새로운 기능 테스트: OCP는 수정보다 코드 확장을 촉진하여 전체 코드베이스에 영향을 주지 않고 새로운 기능을 하나의 단위로 더 쉽게 테스트할 수 있도록 해줍니다.

리스코프 대체 원칙

리스코프 대체 원칙은 서브클래스의 객체가 코드를 손상시키지 않고 슈퍼클래스의 객체를 대체할 수 있어야 한다는 것입니다. 예를 들어 이것이 어떻게 작동하는지 분석해 보겠습니다. L이 P의 하위 클래스인 경우 L의 객체는 시스템을 손상시키지 않고 P의 객체를 대체해야 합니다. 이는 서브클래스가 시스템을 손상시키지 않는 방식으로 슈퍼클래스 메서드를 재정의할 수 있어야 함을 의미합니다.

실제로는 Liskov 대체 원칙에 따라 다음 조건이 준수됩니다.

  • 하위 클래스는 코드를 손상시키지 않고 상위 클래스의 메서드를 재정의해야 합니다
  • 하위 클래스는 상위 클래스의 동작에서 벗어나서는 안 됩니다. 즉, 하위 클래스는 기능을 추가할 수만 있고 상위 클래스 기능을 변경하거나 제거할 수는 없습니다.
  • 상위 클래스의 인스턴스와 작동하는 코드는 클래스가 변경되었다는 사실을 알 필요 없이 하위 클래스의 인스턴스와 작동해야 합니다

이제 JavaScript 코드 샘플을 사용하여 Liskov 대체 원칙을 설명할 차례입니다. 살펴보세요:

class Person {
    constructor(name, dateOfBirth, height, country){
      this.name = name
      this.dateOfBirth = dateOfBirth
      this.height = height
      this.country = country
  }
}

class PersonUtils {
  static calculateAge(dob) { 
    const today = new Date(); 
    const birthDate = new Date(dob);

    let age = today.getFullYear() - birthDate.getFullYear(); 
    const monthDiff = today.getMonth() - birthDate.getMonth();

    if(monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { 
      age--; 
    }
    return age; 
  } 
}

const person = new Person("John", new Date(1994, 11, 23), "6ft", "USA"); 
console.log("Age: " + PersonUtils.calculateAge(person.dateOfBirth));

class PersonService {
  getPersonCountry(){
    console.log(this.country)    
  }
  greetPerson(){
    console.log("Hi " + this.name)
  }
}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

위의 코드 조각에서는 두 개의 하위 클래스(자전거와 자동차)와 하나의 슈퍼 클래스(차량)를 만들었습니다. 이 기사의 목적을 위해 우리는 슈퍼클래스에 대해 단일 메서드(OnEngine)를 구현했습니다.

LSP의 핵심 조건 중 하나는 하위 클래스가 코드를 손상시키지 않고 상위 클래스의 기능을 재정의해야 한다는 것입니다. 이를 염두에 두고 방금 본 코드 조각이 어떻게 Liskov 대체 원칙을 위반하는지 살펴보겠습니다. 실제로 자동차는 엔진이 있어서 엔진을 켤 수 있지만, 자전거는 기술적으로 엔진이 없어 엔진을 켤 수 없습니다. 따라서 Bicycle은 코드를 손상시키지 않고 Vehicle 클래스의 OnEngine 메서드를 재정의할 수 없습니다.

이제 Liskov 대체 원칙을 위반하는 코드 섹션을 확인했습니다. Car 클래스는 슈퍼클래스의 OnEngine 기능을 재정의하고 이를 다른 차량(예: 비행기)과 구별하는 방식으로 구현할 수 있으며 코드가 손상되지 않습니다. Car 클래스는 Liskov 대체 원칙을 충족합니다.

아래 코드 조각에서는 Liskov 대체 원칙을 준수하도록 코드를 구성하는 방법을 설명합니다.

class Person {
    constructor(name, age, height, country){
      this.name = name
      this.age = age
      this.height = height
      this.country = country
  }
  getPersonCountry(){
    console.log(this.country)    
  }
  greetPerson(){
    console.log("Hi " + this.name)
  }
  static calculateAge(dob) { 
    const today = new Date(); 
    const birthDate = new Date(dob);

    let age = today.getFullYear() - birthDate.getFullYear(); 
    const monthDiff = today.getMonth() - birthDate.getMonth();

    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { 
      age--; 
    }
    return age; 
  } 
}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

다음은 일반 기능인 move를 포함하는 Vehicle 클래스의 기본 예입니다. 모든 차량이 움직인다는 것이 일반적인 믿음입니다. 그들은 단지 다른 메커니즘을 통해 움직일 뿐입니다. LSP를 설명할 한 가지 방법은 move() 메서드를 재정의하고 특정 차량(예: Car)이 움직이는 방식을 묘사하는 방식으로 구현하는 것입니다.

이를 위해 다음과 같이 Vehicle 클래스를 확장하고 자동차의 움직임에 맞게 이동 메서드를 재정의하는 Car 클래스를 만들겠습니다.

class Person {
    constructor(name, dateOfBirth, height, country){
      this.name = name
      this.dateOfBirth = dateOfBirth
      this.height = height
      this.country = country
  }
}

class PersonUtils {
  static calculateAge(dob) { 
    const today = new Date(); 
    const birthDate = new Date(dob);

    let age = today.getFullYear() - birthDate.getFullYear(); 
    const monthDiff = today.getMonth() - birthDate.getMonth();

    if(monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { 
      age--; 
    }
    return age; 
  } 
}

const person = new Person("John", new Date(1994, 11, 23), "6ft", "USA"); 
console.log("Age: " + PersonUtils.calculateAge(person.dateOfBirth));

class PersonService {
  getPersonCountry(){
    console.log(this.country)    
  }
  greetPerson(){
    console.log("Hi " + this.name)
  }
}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

다른 하위 차량 클래스(예: 비행기)에서도 이동 메서드를 구현할 수 있습니다.

이를 수행하는 방법은 다음과 같습니다.

class Rectangle { 
  constructor(width, height) {
    this.width = width; 
    this.height = height; 
  } 
  area() { 
  return this.width * this.height; 
  } 
} 

class ShapeProcessor { 
    calculateArea(shape) { 
    if (shape instanceof Rectangle) { 
    return shape.area(); 
    } 
  }
}  
const rectangle = new Rectangle(10, 20); const shapeProcessor = new ShapeProcessor(); console.log(shapeProcessor.calculateArea(rectangle)); 
로그인 후 복사
로그인 후 복사

위의 두 가지 예에서는 상속 및 메서드 재정의와 같은 주요 개념을 설명했습니다.

주의: 하위 클래스가 상위 클래스에 이미 정의된 메서드를 구현할 수 있도록 하는 프로그래밍 기능을 메서드 재정의라고 합니다.

다음과 같이 정리하고 모든 것을 정리합시다.

class Shape {
  area() {
    console.log("Override method area in subclass");
  }
}

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 * this.radius * this.radius;
  }
}

class ShapeProcessor {
  calculateArea(shape) {
    return shape.area();
  }
}

const rectangle = new Rectangle(20, 10);
const circle = new Circle(2);
const shapeProcessor = new ShapeProcessor();

console.log(shapeProcessor.calculateArea(rectangle));
console.log(shapeProcessor.calculateArea(circle));
로그인 후 복사
로그인 후 복사

이제 상위 클래스에서 단일 기능을 상속 및 재정의하고 요구 사항에 따라 구현하는 2개의 하위 클래스가 있습니다. 이 새로운 구현은 코드를 손상시키지 않습니다.

인터페이스 분리 원칙(ISP)

인터페이스 분리 원칙은 클라이언트가 사용하지 않는 인터페이스에 의존하도록 강요해서는 안 된다는 것입니다. 클라이언트가 필요하지 않은 메서드를 구현하도록 강요하는 거대하고 모놀리식 인터페이스를 갖기보다는 특정 클라이언트와 관련된 더 작고 더 구체적인 인터페이스를 만들기를 원합니다.

인터페이스를 간결하게 유지하면 코드 기반의 디버깅, 유지 관리, 테스트 및 확장이 더 쉬워집니다. ISP가 없으면 대규모 인터페이스의 한 부분이 변경되면 코드 베이스의 관련되지 않은 부분이 강제로 변경될 수 있으며, 이로 인해 대부분의 경우 코드 베이스의 크기에 따라 어려운 작업이 될 수 있는 코드 리팩토링을 수행하게 됩니다.

JavaScript는 Java와 같은 C 기반 프로그래밍 언어와 달리 인터페이스에 대한 기본 지원 기능이 없습니다. 그러나 JavaScript로 인터페이스를 구현하는 기술이 있습니다.

인터페이스는 클래스가 구현해야 하는 메서드 시그니처 집합입니다.

JavaScript에서는 다음과 같이 메소드 이름과 함수 서명이 있는 객체로 인터페이스를 정의합니다.

class Person {
    constructor(name, age, height, country){
      this.name = name
      this.age = age
      this.height = height
      this.country = country
  }
  getPersonCountry(){
    console.log(this.country)    
  }
  greetPerson(){
    console.log("Hi " + this.name)
  }
  static calculateAge(dob) { 
    const today = new Date(); 
    const birthDate = new Date(dob);

    let age = today.getFullYear() - birthDate.getFullYear(); 
    const monthDiff = today.getMonth() - birthDate.getMonth();

    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { 
      age--; 
    }
    return age; 
  } 
}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

JavaScript로 인터페이스를 구현하려면 클래스를 생성하고 인터페이스에 지정된 것과 동일한 이름과 서명을 가진 메서드가 포함되어 있는지 확인하세요.

class Person {
    constructor(name, dateOfBirth, height, country){
      this.name = name
      this.dateOfBirth = dateOfBirth
      this.height = height
      this.country = country
  }
}

class PersonUtils {
  static calculateAge(dob) { 
    const today = new Date(); 
    const birthDate = new Date(dob);

    let age = today.getFullYear() - birthDate.getFullYear(); 
    const monthDiff = today.getMonth() - birthDate.getMonth();

    if(monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { 
      age--; 
    }
    return age; 
  } 
}

const person = new Person("John", new Date(1994, 11, 23), "6ft", "USA"); 
console.log("Age: " + PersonUtils.calculateAge(person.dateOfBirth));

class PersonService {
  getPersonCountry(){
    console.log(this.country)    
  }
  greetPerson(){
    console.log("Hi " + this.name)
  }
}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

이제 JavaScript에서 인터페이스를 만들고 사용하는 방법을 알아냈습니다. 다음으로 해야 할 일은 JavaScript에서 인터페이스를 분리하는 방법을 설명하여 모든 인터페이스가 어떻게 조화를 이루고 코드를 더 쉽게 유지 관리할 수 있는지 확인하는 것입니다.

다음 예에서는 프린터를 사용하여 인터페이스 분리 원칙을 설명하겠습니다.

프린터, 스캐너, 팩스가 있다고 가정하고 이러한 개체의 기능을 정의하는 인터페이스를 만들어 보겠습니다.

class Rectangle { 
  constructor(width, height) {
    this.width = width; 
    this.height = height; 
  } 
  area() { 
  return this.width * this.height; 
  } 
} 

class ShapeProcessor { 
    calculateArea(shape) { 
    if (shape instanceof Rectangle) { 
    return shape.area(); 
    } 
  }
}  
const rectangle = new Rectangle(10, 20); const shapeProcessor = new ShapeProcessor(); console.log(shapeProcessor.calculateArea(rectangle)); 
로그인 후 복사
로그인 후 복사

위 코드에서는 이러한 모든 기능을 정의하는 하나의 큰 인터페이스를 갖는 것에 대해 분리되거나 분리된 인터페이스 목록을 만들었습니다. 이러한 기능을 더 작은 부분과 보다 구체적인 인터페이스로 나누어 다양한 클라이언트가 필요한 메서드만 구현하고 다른 부분은 모두 유지할 수 있도록 합니다.

다음 단계에서는 이러한 인터페이스를 구현하는 클래스를 생성하겠습니다. 인터페이스 분리 원칙에 따라 각 클래스는 필요한 메서드만 구현합니다.

문서만 인쇄할 수 있는 기본 프린터를 구현하려면 다음과 같이 프린터 인터페이스를 통해 print() 메서드를 구현하면 됩니다.

class Shape {
  area() {
    console.log("Override method area in subclass");
  }
}

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 * this.radius * this.radius;
  }
}

class ShapeProcessor {
  calculateArea(shape) {
    return shape.area();
  }
}

const rectangle = new Rectangle(20, 10);
const circle = new Circle(2);
const shapeProcessor = new ShapeProcessor();

console.log(shapeProcessor.calculateArea(rectangle));
console.log(shapeProcessor.calculateArea(circle));
로그인 후 복사
로그인 후 복사

이 클래스는 PrinterInterface만 구현합니다. 스캔이나 팩스 방식을 구현하지 않습니다. 인터페이스 분리 원칙에 따라 클라이언트(이 경우 Printer 클래스)는 복잡성을 줄이고 소프트웨어 성능을 향상시켰습니다.

종속성 역전 원리(DIP)

이제 마지막 원칙인 종속성 반전 원칙을 살펴보겠습니다. 이 원칙은 상위 수준 모듈(비즈니스 로직)이 하위 수준 모듈(구체)에 직접 의존하기보다는 추상화에 의존해야 한다는 것을 의미합니다. 이는 코드 종속성을 줄이는 데 도움이 되며 개발자에게 복잡한 문제 없이 더 높은 수준에서 애플리케이션을 수정하고 확장할 수 있는 유연성을 제공합니다.

종속성 역전 원칙이 직접적인 종속성보다 추상화를 선호하는 이유는 무엇입니까? 추상화를 도입하면 변경으로 인한 잠재적 영향이 줄어들고, 테스트 가능성이 향상되고(구체적인 구현 대신 추상화 모의) 코드의 유연성이 높아지기 때문입니다. 이 규칙을 사용하면 모듈식 접근 방식을 통해 소프트웨어 구성 요소를 더 쉽게 확장할 수 있으며 상위 수준 논리에 영향을 주지 않고 하위 수준 구성 요소를 수정할 수도 있습니다.

DIP를 준수하면 코드의 유지 관리, 확장 및 확장이 더 쉬워지고 코드 변경으로 인해 발생할 수 있는 버그를 막을 수 있습니다. 개발자는 클래스 간 긴밀한 결합 대신 느슨한 결합을 사용하는 것이 좋습니다. 일반적으로 팀은 직접적인 종속성보다 추상화를 우선시하는 사고방식을 수용함으로써 파급 중단을 일으키지 않고 새로운 기능을 적용 및 추가하거나 기존 구성 요소를 변경할 수 있는 민첩성을 얻게 됩니다. JavaScript에서는 다음과 같이 종속성 주입 접근 방식을 사용하여 DIP를 구현할 수 있습니다.

class Person {
    constructor(name, age, height, country){
      this.name = name
      this.age = age
      this.height = height
      this.country = country
  }
  getPersonCountry(){
    console.log(this.country)    
  }
  greetPerson(){
    console.log("Hi " + this.name)
  }
  static calculateAge(dob) { 
    const today = new Date(); 
    const birthDate = new Date(dob);

    let age = today.getFullYear() - birthDate.getFullYear(); 
    const monthDiff = today.getMonth() - birthDate.getMonth();

    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { 
      age--; 
    }
    return age; 
  } 
}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

위의 기본 예에서 Application 클래스는 데이터베이스 추상화에 의존하는 상위 수준 모듈입니다. 우리는 MySQLDatabase와 MongoDBDatabase라는 두 개의 데이터베이스 클래스를 만들었습니다. 데이터베이스는 하위 수준 모듈이며 해당 인스턴스는 애플리케이션 자체를 수정하지 않고 애플리케이션 런타임에 주입됩니다.

결론

SOLID 원칙은 확장 가능하고 유지 관리가 가능하며 견고한 소프트웨어 설계를 위한 기본 구성 요소입니다. 이러한 원칙 세트는 개발자가 깔끔하고 모듈식이며 적응 가능한 코드를 작성하는 데 도움이 됩니다.

SOLID 원칙은 응집력 있는 기능, 수정 없는 확장성, 객체 대체, 인터페이스 분리, 구체적인 종속성에 대한 추상화를 촉진합니다. 버그를 방지하고 모든 이점을 누릴 수 있도록 SOLID 원칙을 코드에 통합하세요.


LogRocket: 컨텍스트를 이해하여 JavaScript 오류를 더 쉽게 디버깅합니다.

코드 디버깅은 항상 지루한 작업입니다. 하지만 오류를 더 많이 이해할수록 오류를 수정하는 것이 더 쉬워집니다.

LogRocket을 사용하면 이러한 오류를 새롭고 독특한 방식으로 이해할 수 있습니다. 당사의 프런트엔드 모니터링 솔루션은 JavaScript 프런트엔드에 대한 사용자 참여를 추적하여 오류를 초래한 사용자의 행동을 정확하게 확인할 수 있는 기능을 제공합니다.

SOLID principles for JavaScript

LogRocket은 콘솔 로그, 페이지 로드 시간, 스택 추적, 헤더 본문이 포함된 느린 네트워크 요청/응답, 브라우저 메타데이터 및 사용자 정의 로그를 기록합니다. JavaScript 코드의 영향을 이해하는 것은 결코 쉬운 일이 아닙니다!

무료로 사용해 보세요.

위 내용은 JavaScript의 견고한 원칙의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿