掌握Python中的坚实原则:可扩展编码的指南
#网络开发人员 #初学者 #python #designpatterns

可靠的原理是一组软件设计原则,旨在帮助软件开发人员编写可扩展和可维护的代码。这些软件设计的标准原理是Single responsibility principleOpen-closed principleLiskov substitution principleInterface segregation principleDependency inversion principle。在采用扎实的原则之前,软件开发人员面临着与易于维护和理解的编写代码相关的几个挑战。使用代码设计的技术人员面临与代码变得纠结且难以理解的问题,有时被称为spaghetti code。这使得很难进行更改或在代码库中添加新功能,并可能导致错误和其他意外问题。

固体先例的另一个主要问题是代码刚度,在该代码库中更改代码库将需要更改代码库的许多其他部分。这使得在不引入错误或破坏现有功能的情况下,很难更改代码库。我们还遇到了模块化,其中代码没有分为较小,更易于管理的组件。这使重复使用代码变得困难,并使代码库更难理解和维护。

罗伯特·马丁(Robert C.凭借坚实的信条,软件开发人员可以创建更易于理解,更易于维护和更灵活的代码。
接下来,我们讨论固定原则的组成部分。

什么是扎实的设计原理

可靠的原理是软件设计的最佳实践,以帮助软件开发人员编写可维护,可扩展和灵活的代码。

让我们更深入地研究以一些python代码示例理解固体原理,以将固体信条推向我们的血液:

五个坚实的原理是:

  • 单一责任原则(SRP):班级应具有单一的责任或一个工作
  • 开放/关闭原则(OCP):应该打开一个班级以进行扩展,但要进行修改。
  • Liskov替代原则(LSP):派生的类应该可以代替其基础类别。
  • 接口隔离原理(ISP):使用接口的客户端/类不应强制依赖他们不使用的接口。
  • 依赖性反转原理(DIP):高级模块不应取决于低级模块;两者都应取决于抽象。

接下来,我们将在这些最佳实践上铺上每个原则,并在这些最佳实践上添加一些Python代码,以帮助我们更好地了解稳定的原理。

单一责任原则

单一责任原则(SRP)是一个基本的坚实原则,指出班级应具有单一的责任或一份工作。该原则有助于使代码更加可维护,可测试和可重复使用。

让我们用医疗保健申请表支撑。

要使用样本Python代码来证明单一职责原则,让我们考虑一下医疗保健应用程序的案例研究。假设我们有一个名为患者的课程,该类别具有以下特性:name,Age,genderblood_grouppatient_idpatient_idmedical_historyappointmentstreatments

但是,Patient类违反了SRP原则,因为它的责任太多。它在一个班级中存储患者datamedical historyappointmentstreatments。一种更好的方法是将患者数据存储和预约时间表的关注点分为两个单独的类。

下面提供了一个代码示例以说明以下内容:

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类可以接受Insuranceclass的实例,该实例将用于计算账单。

让我们创建一个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 ClassesABC类是所有其他抽象类的基类。

theInsurance类包含一个名为calculate_discount的单个abstractmethod。 abstract方法是在abstract类中声明但没有实现的方法。相反,Insurance类的任何子类都必须为此方法提供实现。

在这种情况下,calculate_discount方法采用amount参数,并根据特定的保险单返回折扣的amount。通过将此方法定义为abstractInsurance类中,保险单的任何具体实施都必须提供此方法的实施。

现在,让我们实施两个扩展Insurance类的保险类(HMOPolicyPPOPolicy)。

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 classHMOPolicyPPOPolicy,它们从抽象的Insuranceclass继承。这两个concrete classes implement thecalculate_discountmethod, providing their implementation of the method defined in theabstractclass. Thehmopolicy class overrides thecalculate_discountkountmethod to provide a discount of 20% (0.8) on the givenAmount`。这意味着,如果患者涵盖了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类,该类别在其构造函数中采用三个参数:patientamountinsurance_policypatient参数是代表患者名称的字符串,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方法的实现。

现在,让我们看一下修改后的PatientInsuredPatient类:

`
班级患者:
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(自我,患者:患者):
通过
`
现在,假设我们有两个称为SurgeryMedication的类,它们实现了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类和SurgeryMedication类之间引入抽象。实现此目的的一种方法是定义一个称为TreatmentStrategy的界面,该界面定义了execute_treatment方法:


class TreatmentStrategy(ABC):
@abstractmethod
def execute_treatment(self, patient: Patient):
pass

现在,我们可以修改SurgeryMedication类以实现TreatmentStrategy接口:

`
课程手术(治疗疗法):
def execute_teratment(自我,患者:患者):
#对患者进行手术

类药物(治疗疗法):
def execute_teratment(自我,患者:患者):
#对患者进行药物治疗
`

接下来,我们将HospitalManager 类修改为依赖TreatmentStrategy接口,而不是SurgeryMedication类:

`
班级医师:
def init (self,presents_strategy:治疗疗法):
self.treatment_strategy =治疗_Strategy

def manage_treatment(self, patient: Patient):
    self.treatment_strategy.execute_treatment(patient)

`

这样做,我们倒了依赖性-HospitalManager 阶级现在取决于抽象TreatmentStrategy而不是低级模块SurgeryMedication。这使代码更加灵活,更易于维护,因为我们可以轻松地交换TreatmentStrategy接口的不同实现,而不会影响HospitalManager类。

总的来说,依赖性反转原理有助于减少耦合并提高代码的模块化和可维护性。

概括

可靠的原理是经过良好接受的软件设计原理。通过提供一组编写可维护和可扩展代码的最佳实践,开发了可靠的原则来应对软件设计挑战。通过遵循坚实的原则,软件开发人员可以创建更易于理解,更易于维护和更灵活的代码。

其他设计原则,例如GRASP(一般责任分配软件模式), DRY (不要重复自己),亲吻(保持简单,愚蠢)和YAGNI(您不需要它)要查看这些设计原则如何适合您的项目要求。

注意:
我在Hashnode上发布了原始版本。此副本已修改!