可靠的原理是一组软件设计原则,旨在帮助软件开发人员编写可扩展和可维护的代码。这些软件设计的标准原理是Single responsibility principle
,Open-closed principle
,Liskov substitution principle
,Interface segregation principle
和Dependency inversion principle
。在采用扎实的原则之前,软件开发人员面临着与易于维护和理解的编写代码相关的几个挑战。使用代码设计的技术人员面临与代码变得纠结且难以理解的问题,有时被称为spaghetti code
。这使得很难进行更改或在代码库中添加新功能,并可能导致错误和其他意外问题。
固体先例的另一个主要问题是代码刚度,在该代码库中更改代码库将需要更改代码库的许多其他部分。这使得在不引入错误或破坏现有功能的情况下,很难更改代码库。我们还遇到了模块化,其中代码没有分为较小,更易于管理的组件。这使重复使用代码变得困难,并使代码库更难理解和维护。
罗伯特·马丁(Robert C.凭借坚实的信条,软件开发人员可以创建更易于理解,更易于维护和更灵活的代码。接下来,我们讨论固定原则的组成部分。
什么是扎实的设计原理
可靠的原理是软件设计的最佳实践,以帮助软件开发人员编写可维护,可扩展和灵活的代码。
让我们更深入地研究以一些python代码示例理解固体原理,以将固体信条推向我们的血液:
五个坚实的原理是:
- 单一责任原则(SRP):班级应具有单一的责任或一个工作
- 开放/关闭原则(OCP):应该打开一个班级以进行扩展,但要进行修改。
- Liskov替代原则(LSP):派生的类应该可以代替其基础类别。
- 接口隔离原理(ISP):使用接口的客户端/类不应强制依赖他们不使用的接口。
- 依赖性反转原理(DIP):高级模块不应取决于低级模块;两者都应取决于抽象。
接下来,我们将在这些最佳实践上铺上每个原则,并在这些最佳实践上添加一些Python代码,以帮助我们更好地了解稳定的原理。
单一责任原则
单一责任原则(SRP)是一个基本的坚实原则,指出班级应具有单一的责任或一份工作。该原则有助于使代码更加可维护,可测试和可重复使用。
让我们用医疗保健申请表支撑。
要使用样本Python代码来证明单一职责原则,让我们考虑一下医疗保健应用程序的案例研究。假设我们有一个名为患者的课程,该类别具有以下特性:name
,Age,gender
,blood_group
,patient_id
,patient_id
,medical_history
,appointments
和treatments
。
但是,Patient
类违反了SRP原则,因为它的责任太多。它在一个班级中存储患者data
,medical history
,appointments
和treatments
。一种更好的方法是将患者数据存储和预约时间表的关注点分为两个单独的类。
下面提供了一个代码示例以说明以下内容:
class Patient:
def __init__(self, name, age, gender, blood_group, patient_id, medical_history, appointments, treatments):
self.name = name
self.age = age
self.gender = gender
self.blood_group = blood_group
self.patient_id = patient_id
self.medical_history = medical_history
self.appointments = appointments
self.treatments = treatments
def get_patient_data(self):
# return patient data
def get_medical_history(self):
# return medical history
def get_appointments(self):
# return appointments
def get_treatments(self):
# return treatments
def schedule_appointment(self, date):
# schedule an appointment
def add_treatment(self, treatment):
# add a new treatment
前面的Patient
类违反了SRP原则,因为它的责任太多了。我们可以通过创建PatientData
类和AppointmentScheduler
类来重构代码和分开关注。
class PatientData:
def __init__(self, name, age, gender, blood_group, patient_id, medical_history, treatments):
self.name = name
self.age = age
self.gender = gender
self.blood_group = blood_group
self.patient_id = patient_id
self.medical_history = medical_history
self.treatments = treatments
def get_patient_data(self):
# return patient data
def get_medical_history(self):
# return medical history
def get_treatments(self):
# return treatments
class AppointmentScheduler:
def __init__(self, patient_data):
self.patient_data = patient_data
self.appointments = []
def get_appointments(self):
# return appointments
def schedule_appointment(self, date):
# schedule an appointment
在前面的重构代码中,我们分开了关注点并创建了两个类。 Open-closed principle
班级存储患者数据,病史和治疗,而AppointmentScheduler
班级处理约会时间表。
实施了单个职责原则,我们使代码更加可维护,可重复使用。
列表中的下一个是OOPEN/封闭原理。
开放/关闭原则
开放/封闭的原理(OCP)是坚实原理的另一个组成部分,该原理指出软件实体(类,模块,功能等)应打开以进行扩展,但要关闭以进行修改。换句话说,我们应该能够在不修改其源代码的情况下扩展类的行为。该原则促进了代码可重复性和可维护性。
让我们继续使用Healthcare Application用例,并使用Python代码演示开放/封闭的原则。假设我们有一个名为Billing
的课程,可以计算患者治疗的账单。
这是可以写入Billing
类的方式而不遵守开放/封闭的原则:
class Billing:
def __init__(self, patient, amount):
self.patient = patient
self.amount = amount
def calculate_bill(self):
# calculate bill based on treatment amount
if self.patient.insurance == 'HMO':
self.amount = self.amount * 0.8
elif self.patient.insurance == 'PPO':
self.amount = self.amount * 0.9
return self.amount
前面的Billing
类违反了OCP原则,因为如果我们想添加新型的保险,我们必须修改Billing
类的源代码,这可以引入错误并使代码难以维护。
要遵守OCP原则,我们可以修改Billing
类以使用策略模式。我们可以创建一个抽象的Insurance
类,并具有特定的保险类扩展。然后,Billing
类可以接受Insurance
class的实例,该实例将用于计算账单。
让我们创建一个Insurance
抽象类:
from abc import ABC, abstractmethod
class Insurance(ABC):
@abstractmethod
def calculate_discount(self, amount):
pass
使用Python Standard Library的abc
模块,以Abstract Base Classes
为代表的Insurance
定义了abstract
类,该模块代表Abstract Base Classes
。 ABC
类是所有其他抽象类的基类。
theInsurance
类包含一个名为calculate_discount
的单个abstract
method。 abstract
方法是在abstract
类中声明但没有实现的方法。相反,Insurance
类的任何子类都必须为此方法提供实现。
在这种情况下,calculate_discount
方法采用amount
参数,并根据特定的保险单返回折扣的amount
。通过将此方法定义为abstract
在Insurance
类中,保险单的任何具体实施都必须提供此方法的实施。
现在,让我们实施两个扩展Insurance
类的保险类(HMOPolicy
和PPOPolicy
)。
class HMOPolicy(Insurance):
def calculate_discount(self, amount):
return amount * 0.8
class PPOPolicy(Insurance):
def calculate_discount(self, amount):
return amount * 0.9
前面的摘要定义了两个concrete
classHMOPolicy
和PPOPolicy
,它们从抽象的Insurance
class继承。这两个concrete classes implement the
calculate_discountmethod, providing their implementation of the method defined in the
abstractclass. The
hmopolicy class overrides the
calculate_discountkountmethod to provide a discount of 20% (0.8) on the given
Amount`。这意味着,如果患者涵盖了HMO保险单,他们将获得20%的医疗账单折扣。
同样,PPOPolicy
类覆盖了calculate_discount
方法,以提供给定金额的10%(0.9)折扣。这意味着,如果患者涵盖了PPO保险单,他们将获得10%的医疗账单折扣。
最后,修改后的计费类现在看起来像:
`
班级计费:
def init (自我,患者,金额,保险_policy):
self.patient =患者
self.amount =金额
self.insurance_policy = Insurance_policy
def calculate_bill(self):
# calculate bill based on treatment amount
return self.insurance_policy.calculate_discount(self.amount)
`
前面的代码定义了Billing
类,该类别在其构造函数中采用三个参数:patient
,amount
和insurance_policy
。 patient
参数是代表患者名称的字符串,amount
参数是代表处理成本的数值值,而insurance_policy
parameter是实现Insurance
类(或其子类)的对象。
calculate_bill()
方法根据保险单提供的治疗费用和折扣来计算患者的账单。该方法将insurance_policy
对象的the calculate_discount()
Method称为 calculate_discount()
method,并通过处理费用为(self.amount)
作为该方法的参数。 insurance_policy
对象的calculate_discount()
方法根据保险单的类型来计算the discount
,并返回折扣金额。简而言之,Billing
类根据治疗成本和他们拥有的保险单的类型来计算患者的账单。
通过实施开放/封闭的原则,Billing
类更容易维护和扩展。现在,我们可以通过创建新的类来添加新的保险类型,以扩展Insurance
类,而无需修改Billing
类的源代码。
这并不是全部具有扎实的原则,我们在列表中列出了Liskov替代原则。
Liskov替代原则
Liskov替代原则指出,超类的对象应被替换为其子类的对象,而不会影响程序的正确性。换句话说,如果一个函数以超类作为参数的对象,它也应该能够接受其子类的对象而不会引起任何错误或意外行为。
让我们继续以医疗保健应用示例,并使用Python代码演示Liskov替代原则。假设我们有一个名为Patient
的课程,其方法称为pay_bill
,该课程为患者的治疗支付费用。
没有实施Liskov替代原则,Patient
类可以以这种方式写:
`
班级患者:
def init (自我,名称,平衡):
self.name =名称
self.balance = balance
def pay_bill(self, amount):
if self.balance >= amount:
self.balance -= amount
print(f"Paid {amount} for the treatment")
else:
print("Insufficient balance. Please add funds")
`
以上Patient
类违反了LSP原理,因为如果我们创建一个具有不同实现的pay_bill
方法的子类,则当传递到期望Patient
类的对象的函数时,可能会导致意外行为。
要遵守LSP原理,我们可以修改Patient
类以对pay_bill
方法具有更一般的行为。我们可以创建一个名为InsuredPatient
的新子类,该子类扩展Patient
类并具有pay_bill
方法的实现。
现在,让我们看一下修改后的Patient
和InsuredPatient
类:
`
班级患者:
def init (自我,名称,平衡):
self.name =名称
self.balance = balance
def pay_bill(self, amount):
self.balance -= amount
print(f"Paid {amount} for the treatment")
class versuredpatient(患者):
def init (自我,名称,余额,保险_policy):
super()。 init (名称,平衡)
self.insurance_policy = Insurance_policy
def pay_bill(self, amount):
discounted_amount = self.insurance_policy.calculate_discount(amount)
super().pay_bill(discounted_amount)
`
在前面的修改代码中,我们创建了一个名为InsuredPatient
的新子类,该子类扩展了Patient
类并具有pay_bill
方法的实现。 InsuredPatient
类还接受Insurance
类的实例,该类别将用于计算折扣账单金额。
通过遵守Liskov替代原则,我们现在可以将InsuredPatient
Class的对象传递到一个期望Patient
类的对象而不会引起任何意外行为的函数中。
向右移动,我们有界面隔离原理要讨论。
界面隔离原理
接口隔离原理(ISP)指出,不应强迫客户端依靠他们不使用的接口。这意味着不应强制使用他们不需要的方法实现界面。
在医疗保健应用程序的上下文中,假设我们有一个称为Treatment
的接口,该接口定义了一种称为treat_patient
的方法,该方法将Patient
对象作为参数作为参数并对患者执行一些治疗。
以下是如何编写Treatment
interface的示例:
`
课堂治疗(ABC):
@AbstractMethod
def treat_patient(自我,患者:患者):
通过
`
现在,假设我们有两个称为Surgery
和Medication
的类,它们实现了Treatment
接口。 Surgery
类通过对患者进行手术来实现treat_patient
方法,而Medication
类通过对患者进行药物治疗来实现treat_patient
方法。
但是,上述设计的问题是,并非所有患者都需要手术或药物治疗。例如,手臂骨折的患者可能只需要铸造而不需要手术或药物。因此,最好为每种类型的治疗创建单独的接口。
遵循接口隔离原理,这就是修改的代码的样子:
class Surgery(ABC):
@abstractmethod
def perform_surgery(self, patient: Patient):
pass
类药物(ABC):
@AbstractMethod
def administer_medication(自我,患者:患者):
通过
`
现在我们有两个单独的接口-Surgery
和Medication
。这些界面定义了针对其各自治疗的方法。这允许实现这些接口的类仅实现他们需要的方法,而不是被迫实现不必要的方法。
随着界面隔离原则的遵守,我们提高了代码的模块化和可维护性,使将来更容易在不影响现有代码的情况下添加新处理。
有趣的是,扎实的故事并非以接口隔离原则结束,我们仍然具有依赖性反转。
依赖性反转原则
依赖性反转原则指出,高级模块不应取决于低级模块,都应取决于抽象。换句话说,细节应取决于抽象,而不是相反。
在医疗保健应用程序的背景下,假设我们有HospitalManager
班级负责管理医院所有患者的治疗方法。此类目前对Surgery
和Medication
类具有依赖性,该类是低级模块。
要遵守依赖性反转原理,我们需要在HospitalManager
类和Surgery
和Medication
类之间引入抽象。实现此目的的一种方法是定义一个称为TreatmentStrategy
的界面,该界面定义了execute_treatment
方法:
class TreatmentStrategy(ABC):
@abstractmethod
def execute_treatment(self, patient: Patient):
pass
现在,我们可以修改Surgery
和Medication
类以实现TreatmentStrategy
接口:
`
课程手术(治疗疗法):
def execute_teratment(自我,患者:患者):
#对患者进行手术
类药物(治疗疗法):
def execute_teratment(自我,患者:患者):
#对患者进行药物治疗
`
接下来,我们将HospitalManager
类修改为依赖TreatmentStrategy
接口,而不是Surgery
和Medication
类:
`
班级医师:
def init (self,presents_strategy:治疗疗法):
self.treatment_strategy =治疗_Strategy
def manage_treatment(self, patient: Patient):
self.treatment_strategy.execute_treatment(patient)
`
这样做,我们倒了依赖性-HospitalManager
阶级现在取决于抽象TreatmentStrategy
而不是低级模块Surgery
和Medication
。这使代码更加灵活,更易于维护,因为我们可以轻松地交换TreatmentStrategy
接口的不同实现,而不会影响HospitalManager
类。
总的来说,依赖性反转原理有助于减少耦合并提高代码的模块化和可维护性。
概括
可靠的原理是经过良好接受的软件设计原理。通过提供一组编写可维护和可扩展代码的最佳实践,开发了可靠的原则来应对软件设计挑战。通过遵循坚实的原则,软件开发人员可以创建更易于理解,更易于维护和更灵活的代码。
其他设计原则,例如GRASP
(一般责任分配软件模式), DRY
(不要重复自己),亲吻(保持简单,愚蠢)和YAGNI
(您不需要它)要查看这些设计原则如何适合您的项目要求。
注意:
我在Hashnode上发布了原始版本。此副本已修改!