代码大全中保持松散耦合的demo
Q:
假设你有一段子程序,通过输入雇用日期和工作级别来查询员工每年获得的休假数量。这段子程序被命名为LookupVacationBenefit()。假设在另一个模块里已经有了一个employee对象,其中包含了雇用日期和工作级别,以及其他一些信息,该模块把这种对象传给LookupVacationBenefit()。
按照其他耦合标准,这两个模块间的耦合关系看上去是很松散的。这两个模块之间的employee连接是可见的,而且这里只存在一个连接。现在假设你需要在第三个模块中使用 LookupVacationBenefit(),这一模块中没有 employee 对象,但却包含雇用日期和工作级别。这样,LookupVacationBenefit()一下子就变得不太友好了,它无法和新的模块协同工作。
要使第三个模块能使用LookupVacationBenefit(),它就必须了解Employee类。它可能去临时拼凑一个只包含两个字段的employee 对象,但是这就需要了解 LookupvacationBenefit()的内部机制,也就是说,只有这两个字段才是 LookupvacationBenefit()所需的。这样的解决方案非常牵强,而且也十分丑陋。第二种解决方案可能是对 LookupVacationBenefit()做出修改,使它以雇用日期和工作级别作为参数,而不用employee。无论怎样,原有的那个模块与过去相比都已经变得更不灵活了。
从这个故事得到的好消息是,可以让一个不友好的模块变友好,只要把它变得更灵活--在这个例子里,这件事是通过明确以雇用日期和工作级别取代employee作为参数来实现的。
A:
在软件设计中,保持模块间的低耦合和高内聚是提高代码可维护性和可扩展性的关键。针对你描述的情况,以下是一些改进 LookupVacationBenefit() 函数的建议,使其更加灵活和友好:
1. 重构函数参数
将 LookupVacationBenefit() 函数的参数从 employee 对象更改为所需的具体字段(即雇用日期和工作级别)。这样可以减少函数对 employee 对象的依赖,使其更易于在不同的模块中重用。
def LookupVacationBenefit(hire_date, job_level):
# 计算休假数量的逻辑
vacation_days = calculate_vacation_days(hire_date, job_level)
return vacation_days
package main
import (
"fmt"
"time"
)
// Employee 结构体包含员工信息
type Employee struct {
HireDate time.Time
JobLevel string
}
// LookupVacationBenefit 根据员工的雇佣日期和工作级别查找休假天数
func LookupVacationBenefit(employee Employee) int {
// 示例计算逻辑:根据工作年限和工作级别计算休假天数
yearsOfService := time.Since(employee.HireDate).Hours() / (24 * 365)
vacationDays := int(yearsOfService * 2) // 假设每年2天休假
if employee.JobLevel == "Senior" {
vacationDays += 5 // 高级员工额外5天
}
return vacationDays
}
func main() {
employee := Employee{
HireDate: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
JobLevel: "Senior",
}
days := LookupVacationBenefit(employee)
fmt.Printf("Employee has %d vacation days\n", days)
}
package main
import (
"fmt"
"time"
)
// LookupVacationBenefit 根据雇佣日期和工作级别查找休假天数
func LookupVacationBenefit(hireDate time.Time, jobLevel string) int {
yearsOfService := time.Since(hireDate).Hours() / (24 * 365)
vacationDays := int(yearsOfService * 2)
if jobLevel == "Senior" {
vacationDays += 5
}
return vacationDays
}
func main() {
hireDate := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
jobLevel := "Senior"
days := LookupVacationBenefit(hireDate, jobLevel)
fmt.Printf("Employee has %d vacation days\n", days)
}
2. 使用依赖注入
通过依赖注入(Dependency Injection, DI),可以将所需的依赖(如 employee 对象)传递给函数或类,而不是让函数或类自己查找这些依赖。这有助于降低模块间的耦合度。
class VacationService:
def __init__(self, employee):
self.employee = employee
def get_vacation_days(self):
return LookupVacationBenefit(self.employee.hire_date, self.employee.job_level)
def LookupVacationBenefit(hire_date, job_level):
# 计算休假数量的逻辑
vacation_days = calculate_vacation_days(hire_date, job_level)
return vacation_days
package main
import (
"fmt"
"time"
)
type Employee interface {
GetHireDate() time.Time
GetJobLevel() string
}
type ConcreteEmployee struct {
HireDate time.Time
JobLevel string
}
func (e *ConcreteEmployee) GetHireDate() time.Time {
return e.HireDate
}
func (e *ConcreteEmployee) GetJobLevel() string {
return e.JobLevel
}
func LookupVacationBenefit(employee Employee) int {
yearsOfService := time.Since(employee.GetHireDate()).Hours() / (24 * 365)
vacationDays := int(yearsOfService * 2)
if employee.GetJobLevel() == "Senior" {
vacationDays += 5
}
return vacationDays
}
func main() {
employee := &ConcreteEmployee{
HireDate: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
JobLevel: "Senior",
}
days := LookupVacationBenefit(employee)
fmt.Printf("Employee has %d vacation days\n", days)
}
3. 封装 employee 对象
如果 employee 对象包含多个字段,但只有少数字段需要被 LookupVacationBenefit() 函数使用,可以考虑创建一个更小的封装对象或数据传输对象(Data Transfer Object, DTO),只包含必要的字段。
class EmployeeInfo:
def __init__(self, hire_date, job_level):
self.hire_date = hire_date
self.job_level = job_level
def LookupVacationBenefit(employee_info):
return calculate_vacation_days(employee_info.hire_date, employee_info.job_level)
4. 使用接口或抽象类
定义一个接口或抽象类来规范 employee 对象的行为,确保所有实现该接口的类都提供必要的方法和属性。这样可以在不修改 LookupVacationBenefit() 函数的情况下,使用不同类型的 employee 对象。
from abc import ABC, abstractmethod
class Employee(ABC):
@abstractmethod
def get_hire_date(self):
pass
@abstractmethod
def get_job_level(self):
pass
def LookupVacationBenefit(employee):
return calculate_vacation_days(employee.get_hire_date(), employee.get_job_level())
5. 考虑使用工厂模式
如果 employee 对象的创建过程复杂,可以考虑使用工厂模式来封装对象的创建过程。这样,LookupVacationBenefit() 函数只需要关心如何使用 employee 对象,而不需要关心其创建过程。
class EmployeeFactory:
@staticmethod
def create_employee(hire_date, job_level):
return Employee(hire_date, job_level)
def LookupVacationBenefit(hire_date, job_level):
employee = EmployeeFactory.create_employee(hire_date, job_level)
return calculate_vacation_days(employee.hire_date, employee.job_level)
通过这些方法,可以使 LookupVacationBenefit() 函数更加灵活,易于在不同的模块中重用,同时降低模块间的耦合度。
本文来自博客园,作者:易先讯,转载请注明原文链接:https://www.cnblogs.com/gongxianjin/p/18327261

浙公网安备 33010602011771号