JavaScript坚实原则:如何编写可维护代码
#javascript #网络开发人员 #solidprinciples

可靠的原理是罗伯特·C·鲍勃·马丁叔叔提出的一套软件设计。这些原则指导开发人员构建强大的可维护应用程序,同时最大程度地减少变化的成本。

尽管经常将固体原理与面向对象的编程一起使用,但我们可以将它们与其他语言(如JavaScript)一起使用。在本文中,我们将讨论如何在JavaScript中使用坚实的原理并用代码示例演示它们。

有什么坚实的原则?

Single responsibility principle

类,模块或函数应仅负责一个演员。因此,它应该有一个也是唯一的改变。

单一责任原则是最简单的原则之一。但是,开发人员经常误解它,认为模块应该做一个事情。

让我们考虑一个简单的例子来理解这一原则。以下JavaScript代码片段具有名为 ManagyEmployee 的类,以及几个可以管理员工的功能。

class ManageEmployee {

  constructor(private http: HttpClient)
  SERVER_URL = 'http://localhost:5000/employee';

  getEmployee (empId){
     return this.http.get(this.SERVER_URL + `/${empId}`);
  }

  updateEmployee (employee){
     return this.http.put(this.SERVER_URL + `/${employee.id}`,employee);
  }

  deleteEmployee (empId){
     return this.http.delete(this.SERVER_URL + `/${empId}`);
  }

  calculateEmployeeSalary (empId, workingHours){
    var employee = this.http.get(this.SERVER_URL + `/${empId}`);
    return employee.rate * workingHours;
  }

}

以前的代码一目了然,似乎完全不错,许多开发人员将遵循同样的方法而没有任何问题。但是,由于这是两个演员的原因,因此该班级违反了单一责任原则。 getEmployee() UpdateEmployee() deleteemployee()函数与HR管理直接相关,而 calculate employemployeesalary() 功能与财务管理有关。

将来,如果您需要更新人力资源或财务部门的功能,则必须更改 Manage Employeee 类,从而影响两个参与者。因此,管理人员类违反了单一责任原则。您需要将与人力资源和财务部门相关的功能分开,以使代码与单个职责原则兼容。以下代码示例说明了这一点。

class ManageEmployee {

  constructor(private http: HttpClient)
  SERVER_URL = 'http://localhost:5000/employee';

  getEmployee (empId){
     return this.http.get(this.SERVER_URL + `/${empId}`);
  }

  updateEmployee (employee){
     return this.http.put(this.SERVER_URL + `/${employee.id}`,employee);
  }

  deleteEmployee (empId){
     return this.http.delete(this.SERVER_URL + `/${empId}`);
  }

}

class ManageSalaries {

  constructor(private http: HttpClient)
  SERVER_URL = 'http://localhost:5000/employee';

  calculateEmployeeSalary (empId, workingHours){
    var employee = this.http.get(this.SERVER_URL + `/${empId}`);
    return employee.rate * workingHours;
  }

}

Open-closed principle

功能,模块和类应可扩展,但不能修改。

这是实施大规模应用程序时要遵循的重要原则。根据这一原则,我们应该能够轻松地为应用程序添加新功能,但是我们不应引入现有代码的破坏更改。

例如,假设我们已经实现了一个名为 CounculatesAlaries()的函数,该功能使用具有定义的工作角色和小时费率来计算薪水的数组。

class ManageSalaries {
  constructor() {
    this.salaryRates = [
      { id: 1, role: 'developer', rate: 100 },
      { id: 2, role: 'architect', rate: 200 },
      { id: 3, role: 'manager', rate: 300 },
    ];
  }

  calculateSalaries(empId, hoursWorked) {
    let salaryObject = this.salaryRates.find((o) => o.id === empId);
    return hoursWorked * salaryObject.rate;
  }
}

const mgtSalary = new ManageSalaries();
console.log("Salary : ", mgtSalary.calculateSalaries(1, 100));

Output before implementing Open closed principle

直接修改薪金数组将违反开放原理。例如,假设您需要扩展新角色的工资计算。在这种情况下,您需要创建一种单独的方法,以在工资rates 数组中添加工资率,而无需制作原始代码。

class ManageSalaries {
  constructor() {
    this.salaryRates = [
      { id: 1, role: 'developer', rate: 100 },
      { id: 2, role: 'architect', rate: 200 },
      { id: 3, role: 'manager', rate: 300 },
    ];
  }

  calculateSalaries(empId, hoursWorked) {
    let salaryObject = this.salaryRates.find((o) => o.id === empId);
    return hoursWorked * salaryObject.rate;
  }

  addSalaryRate(id, role, rate) {
    this.salaryRates.push({ id: id, role: role, rate: rate });
  }
}

const mgtSalary = new ManageSalaries();
mgtSalary.addSalaryRate(4, 'developer', 250);
console.log('Salary : ', mgtSalary.calculateSalaries(4, 100));

Output after implementing Open-closed principle

Liskov substitution principle

令P( y )为A类型A的对象 y 。对于B类型的对象 x ,其中b是A。

的子类型

您会在整个互联网上找到Liskov替代原则的不同定义,但它们都意味着相同的含义。简单地说,Liskov原则指出,如果他们在应用程序中创建意外行为,我们不应用其子类替换。

例如,考虑一个名为动物的类

class Animal{
  eat() {
    console.log("Animal Eats")
  }
}

现在,我将把动物类扩展到名为 bird 的新类,其功能名为 fly()

class Bird extends Animal{
  fly() {
    console.log("Bird Flies")
  }
}

var parrot = new Bird();
parrot.eat();
parrot.fly();

Output before implementing Liskov Substitution Principle

在上一个示例中,我创建了一个名为鹦鹉的对象, bird class,并称为 eat() fly()方法。由于鹦鹉都能够采用这两种动作,因此将动物类扩展到 bird 类不违反Liskov原则。

现在让我们进一步扩展 bird 级别,并创建一个名为 ostrich的新类。

class Ostrich extends Bird{
  console.log("Ostriches Do Not Fly")
}

var ostrich = new Ostrich();
ostrich.eat();
ostrich.fly();

鸟类的这种扩展类违反了liskov原理,因为鸵鸟不能飞行 - 这可能会在应用程序中产生意外的行为。解决此案例的最佳方法是将 ostrich 类延长 Animal 类。

class Ostrich extends Animal{

  walk() {
    console.log("Ostrich Walks")
  }

}

Output after implementing Liskov Substitution principle

Interface segregation principle

不应将客户推向他们永远不会使用的接口。

该原理与接口有关,并着重于将大界面分解为较小的接口。例如,假设您要开车去学习如何驾驶汽车,它们为您提供了有关驾驶汽车,卡车和火车的大量说明。由于您只需要学习驾驶汽车,因此您不需要所有其他信息。驾驶学校应分配说明,只向您提供特定于汽车的说明。

由于JavaScript不支持接口,因此很难在基于JavaScript的应用程序中采用此原理。但是,我们可以使用JavaScript组成来实施此功能。组成使开发人员可以在不继承整个类的情况下向类添加功能。例如,假设有一个名为 drive Trive -Test 的类,其两个功能命名 startCartest startTruckTest。 CardRivingTest TruckDrivingTest的strong> class 我们必须强迫两个类以实施 startcartest startTrucktest 功能。

Class DrivingTest {
  constructor(userType) {
    this.userType = userType;
  }

  startCarTest() {
    console.log(This is for Car Drivers”’);
  }

  startTruckTest() {
    console.log(This is for Truck Drivers);
  }
}

class CarDrivingTest extends DrivingTest {
  constructor(userType) {
    super(userType);
  }

  startCarTest() {
    return Car Test Started;
  }

  startTruckTest() {
    return null;
  }
}

class TruckDrivingTest extends DrivingTest {
  constructor(userType) {
    super(userType);
  }

  startCarTest() {
    return null;
  }

  startTruckTest() {
    return Truck Test Started;
  }
}

const carTest = new CarDrivingTest(carDriver );
console.log(carTest.startCarTest());
console.log(carTest.startTruckTest());

const truckTest = new TruckDrivingTest( ruckdriver );
console.log(truckTest.startCarTest());
console.log(truckTest.startTruckTest());

Output before implementing Interface Segregation Principle

但是,此实现违反了接口隔离原则,因为我们强迫两个扩展类实施这两个功能。我们可以通过使用组合来为所需类附加功能来解决此问题。

Class DrivingTest {
  constructor(userType) {
    this.userType = userType;
  }
}

class CarDrivingTest extends DrivingTest {
  constructor(userType) {
    super(userType);
  }
}

class TruckDrivingTest extends DrivingTest {
  constructor(userType) {
    super(userType);
  }
}

const carUserTests = {
  startCarTest() {
    return Car Test Started;
  },
};

const truckUserTests = {
  startTruckTest() {
    return Truck Test Started;
  },
};

Object.assign(CarDrivingTest.prototype, carUserTests);
Object.assign(TruckDrivingTest.prototype, truckUserTests);

const carTest = new CarDrivingTest(carDriver );
console.log(carTest.startCarTest());
console.log(carTest.startTruckTest()); // Will throw an exception

const truckTest = new TruckDrivingTest( ruckdriver );
console.log(truckTest.startTruckTest());
console.log(truckTest.startCarTest()); // Will throw an exception

Output after implementing Interface Segregation Principle

现在, cartest.starttrucktest(); 将引发例外,因为 startTruckTest()函数未分配给 cardRivingTest 类。

Dependency inversion principle

高级模块应使用抽象。但是,它们不应依赖低级模块。

依赖性反转是关于解解代码的。遵循此原则将使您灵活地扩展和更改最高级别的应用程序。

关于JavaScript,我们不需要考虑抽象,因为JavaScript是一种动态语言。但是,我们需要确保更高级别的模块不依赖于低级模块。

让我们考虑一个简单的例子来解释依赖性反转的工作方式。假设您在应用程序中使用了Yahoo电子邮件API,现在您需要将其更改为Gmail API。如果您在没有依赖性反转的情况下实现了控制器,则需要对控制器进行一些更改。这是因为多个控制器使用Yahoo API,您需要找到每个实例并进行更新。

class EmailController { 
  sendEmail(emailDetails) { 
    // Need to change this line in every controller that uses YahooAPI.const response = YahooAPI.sendEmail(emailDetails); 
    if (response.status == 200) { 
       return true;
    } else {
       return false;
    }
  }
}

依赖性反转原则可以通过将电子邮件API处理零件移动到单独的控制器中,从而帮助开发人员避免此类昂贵的错误。然后,只有在电子邮件API中发生更改时,您才需要更改该控制器。

class EmailController { 
  sendEmail(emailDetails) { 
    const response = EmailApiController.sendEmail(emailDetails);   
    if (response.status == 200) { 
       return true;
    } else {
       return false;
    }
  }
}

class EmailApiController {
  sendEmail(emailDetails) {
    // Only need to change this controller. return YahooAPI.sendEmail(emailDetails);
  }
}

结论

在本文中,我们讨论了坚实的原理在软件设计中的重要性,以及如何在JavaScript应用程序中采用这些概念。作为开发人员,必须在我们的应用中理解和使用这些核心概念。有时,在使用小型应用程序时,这些原则的好处可能并不明显,但是您一定会知道,一旦您开始从事大型项目,它们就会产生的不同。

我希望这篇文章能帮助您了解扎实的原则。谢谢您的阅读。

Syncfusion JavaScript suiteis构建应用程序所需的唯一套件。它包含一个包装中的80多个高性能,轻巧,模块化和响应性UI组件。下载free trial并立即评估控件。

如果您有任何疑问或评论,则可以通过我们的support forumssupport portalfeedback portal与我们联系。我们总是很乐意为您提供帮助!

推荐资源

相关博客