Должен ли я добавлять новые методы в класс вместо использования принципа единой ответственности


У нас был семинар, на котором я представил своей команде принцип единой ответственности, чтобы мы использовали его в наших проектах. Я использовал следующий популярный пример:

class Employee:
    save()
    calculate_salary()
    generate_report()
И я попросил команду сказать, все ли в порядке с этим классом. Все говорили мне, что все в порядке. Но я вижу здесь три нарушения принципа СРП. Правильно ли я говорю, что все методы должны быть извлечены из класса? Мои рассуждения:

Метод Save () является причиной для изменения, если изменить наш база данных.

Метод Calculate_salary() является причиной для изменения, поскольку политика заработной платы может измениться.

Метод Generate_report () является причиной для изменения, если мы хотим изменить представление отчета (т. е. csv вместо html). Давайте возьмем последний метод. Я придумал следующий класс HtmlReportGenerator.
class HTMLReportGenerator:
    def __init__(self, reportable):
        self.reportable = reportable

    def generate_csv_report()

class CSVReportGenerator:
    def __init__(self, reportable):
        self.reportable = reportable

    def generate_html_report()

Теперь, даже если бизнес-логика этого генератора изменится, это не коснется класса сотрудников, и это было моим главным пунктом. Более того, теперь мы можно повторно использовать эти классы для объектов, отличных от объектов класса Employee.

Но команда придумала другой класс:
class Employee:
    save()
    calculate_salary()
    generate_html_report()
    generate_csv_report()
Они понимают, что нарушают ПДД, но для них это нормально.

И именно здесь у меня не было других идей для борьбы))

Есть какие-нибудь соображения по поводу ситуации?

1 2

1 ответ:

Я согласен с вами, добавляя дополнительные функции, они нарушили как SRP, так и принцип open/close, и каждый раз, когда будет новый тип отчета, они будут нарушать его снова.

Я бы сохранил функцию generate_report (), но добавил параметр из типа интерфейса "ReportType", который имеет функцию generate ().

Это означает, что, например, вы можете вызвать (простите мою Java):

employee.generate_report(new CSVReport())

employee.generate_report(new HTMLReport())

И завтра, если вы хотите добавить отчет XML, вы просто реализуете XMLReport из интерфейс отчета и вызов:

employee.generate_report(new XMLReport())

Это дает вам большую гибкость, не нужно менять сотрудника для новых типов отчетов, и гораздо проще тестировать (например, если бы generate_report имел сложную логику, вы могли бы просто создать класс TestReport, который реализует интерфейс отчета и просто печатает в выходной поток для отладки и вызова generate_report (new TestReport ()))