我们将继续探讨SOLID原则中“O”:开闭原则。该原则指的是一个类应该只允许对扩展开放,但对修改关闭。
这意味着什么呢?这意味着一旦一个类被其他代码使用,你就不应该更改这个类。如果你修改了类,那么依赖于类的代码就有可能被破坏。相反,你应该扩展这个类来添加功能。
让我们通过一个例子来看看这是什么意思。我们将再次使用Login类,因为软件测试人员经常会遇到登录页面。假设有一家公司有很多个不同的团队,都需要为其功能编写UI自动化测试。其中一位测试工程师Joe创建了一个任何人都可以使用的Login类。它将用户名和密码作为变量,并使用它们完成登录:
class Login {
constructor(username, password) {
this.username = username
this.password = password
}
login() {
driver.findElement(By.id('username'))
.sendKeys(this.username)
driver.findElement(By.id('password))
.sendKeys(this.password)
driver.findElement(By.id('submit)).click()
}
}
每个人都认为这个类很有用,因此他们在自己的测试中都调用了该类。
假设现在网站添加了一个新功能,客户可以在登录过程中选择设置一个挑战问题。Joe想要添加处理这一新功能的能力:
class Login {
constructor(username, password, answer) {
this.username = username
this.password = password
}
login() {
driver.findElement(By.id('username'))
.sendKeys(this.username)
driver.findElement(By.id('password'))
.sendKeys(this.password)
driver.findElement(By.id('submit')).click()
}
loginWithChallenge() {
driver.findElement(By.id('username'))
.sendKeys(this.username)
driver.findElement(By.id('password'))
.sendKeys(this.password)
driver.findElement(By.id('submit')).click()
driver.findElement(By.id('answer'))
.sendKeys(this.answer)
driver.findElement(By.id('submitAnswer')).click()
}
}
注意,Login类现在需要第三个参数:一个answer变量。如果Joe做出了这个更改,所有人的测试都将会遭到破坏,因为他们在创建Login类的实例时,现阶段并没有包含这个变量。这样做Joe不会受到其他测试人员的欢迎了!
相反,Joe应该创建一个名为LoginWithChallenge的新类,将它作为建立在Login类基础上的扩展,同时保持Login类不变:
class LoginWithChallenge extends Login {
constructor(username, password, answer) {
super()
this.username = username
this.password = password
this.answer = answer
}
loginWithChallenge() {
this.login(username, password)
driver.findElement(By.id('answer'))
.sendKeys(this.answer)
driver.findElement(By.id('submitAnswer')).click()
}
}
现在测试人员可以毫无障碍地继续调用Login类。当他们准备更新测试以使用新的挑战问题功能时,他们可以修改测试以调用LoginWithChallenge类。Login类对只允许对扩展开放,但对修改关闭。