Рефакторинг по принципу единой ответственности

#java #class #interface #solid-principles #single-responsibility-principle

#java #класс #интерфейс #solid-принципы #принцип единой ответственности

Вопрос:

У меня есть некоторый код, который я хотел бы переработать, чтобы он не нарушал принцип единой ответственности (SRP).

Я понимаю, что приведенный ниже класс может измениться по нескольким причинам:

  • Бизнес-правила для анализа могут измениться
  • Схема метаданных может измениться
  • Метод загрузки может измениться

Тем не менее, мне трудно понять, как я могу разделить отдельные классы.

Engine.java

 package com.example;

import java.util.List;

public interface Engine {
  public List<Recording> analyze(List<String> files);
  public List<Recording> getMetadata(List<Recording> recordings);
  public List<Recording> upload(List<Recording> recordings);
}
 

CallEngine.java

 package com.example;

import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CallEngine implements Engine {

  final static Logger log = LoggerFactory.getLogger(Main.class);

  public List<Recording> analyze(List<String> files) {
    log.info("Analyzing recording files per business rules...");

    List<Recording> recordings = new ArrayList<Recording>();
    return recordings;
  }

  public List<Recording> getMetadata(List<Recording> r) {
    log.info("Retrieving metadata for calls...");
    List<Recording> recordings = new ArrayList<Recording>();
    return recordings;
  }

  public List<Recording> upload(List<Recording> r) {
    log.info("Uploading calls...");
    List<Recording> recordings = new ArrayList<Recording>();
    return recordings;
  }
}
 

Ответ №1:

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

В этом случае вам нужно абстрагировать обязанности за их собственным интерфейсом.

Например…

 public interface Analyzer {
    public List<Recording> analyze(List<String> files);
}
public interface Retriever {
    public List<Recording> getMetadata(List<Recording> recordings);
}
public interface Uploader {
    public List<Recording> upload(List<Recording> r);
}
 

И иметь их в качестве встроенных зависимостей Engine реализации.

 public class CallEngine implements Engine {
    private Analyzer analyzer;
    private Retriever retriever;
    private Uploader uploader;

    public CallEngine(Analyzer analyzer, Retriever retriever, Uploader uploader) {
        this.analyzer = analyzer;
        this.retriever = retriever;
        this.uploader = uploader;        
    }

    public List<Recording> analyze(List<String> files) {
        return analyzer.analyze(files);
    }

    public List<Recording> getMetadata(List<Recording> r) {
        return retriever.getMetadata(r);
    }

    public List<Recording> upload(List<Recording> r) {
        return uploader.upload(r);
    }
}
 

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

Ответ №2:

 public interface Analyzer {
  public void analyze();
}
public interface Retriever {
  public void retrieveMetadata();
}
public interface Uploader {
  public void upload();
}

public class EngineAnalyzer implements Analyzer {

  final static Logger log = LoggerFactory.getLogger(Main.class);

  public List<Recording> recordings;

  EngineAnaylzer(List<String> Files) {

  }

  public void analyze() {
    log.info("Analyzing recording files per business rules...");
  }  

}