Универсальные интерфейсы Java с безопасными реализациями - PullRequest
0 голосов
/ 12 сентября 2010

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

Пример

Предположим, мы хотим построить отчет на основе списка компонентов. Скажем, у нас есть два конкретных типа компонентов:

public interface Component { ... }
public class PDFComponents extends Component { ... }
public class WordComponents extends Component { ... }

Каждый компонент имеет реализацию ReportBuilder, например,

public interface ReportBuilder { ... }
public class PDFReportBuilder extends ReportBuilder { ... }
public class WordReportBuilder extends ReportBuilder { ... }

, который создает конкретные реализации отчетов

public interface Report { ... }
public class PDFReport extends ReportBuilder { ... }
public class WordReport extends ReportBuilder { ... }

Наконец, у нас есть служба, которая находит компоненты и создает отчет по компонентам.

public class ReportService {
    ReportComponentRepository repo;
    List<ReportBuilder> builders;

    public <T extends Report> T getReport(Class<T> reportType) {
        // Get report components. E.g., this might return List<PDFComponent>
        List<Component> reportComponents = repo.getReportComponents(id);

        // Build report from components using one of the registered builders
        for (ReportBuilder builder : builders) {
            if (builder.buildFor(reportType) {
                return builder.buildReport(report);
            }
        }
    }
}

Пример использования сервиса

List<PDFReport> reports = new ReportService().getReport(PDFReport.class);

Вопрос

Теперь к вопросу. Как я могу разработать общий интерфейс ReportBuilder, который обеспечивает безопасность типов для его реализации?

Например, выбирая интерфейс:

public Report buildReport(List<? extends Component> components);

вызовет уродство в его реализации:

public class PDFReportBuilder implements ReportBuilder {

    @Override
    public Report buildReport(List<? extends Component> components) {
         PDFReport report;

         for (Component component : components) {
            if (component instanceOf PDFComponent) {
                // assemble report ... 
                report.includeComponent(component);
            }
        }

        return report;
    }
}

когда мы действительно хотим, чтобы интерфейс для PDFReportBuilder был, например,

 public Report buildReport(List<PDFComponent> component) { ... }

Ответы [ 2 ]

2 голосов
/ 13 сентября 2010

Мне кажется, что вы настраиваете себя на грязную реализацию, имея три параллельные иерархии наследования.Могу я спросить, почему вы не можете объединить общее поведение компонента и ReportBuilder?Фактически вы теряете любую абстракцию компонентов, заставляя вызывающую службу знать подкласс отчета, который они хотят.

Я бы предложил упростить интерфейс путем минимизации или исключения параметров для buildReport ()

 public class ReportService {
    ReportComponentRepository repo;
    List<ReportBuilder> builders;

    public <T extends Report> T getReport(Class<T> reportType) {

        // Build report from components using one of the registered builders
        for (ReportBuilder builder : builders) {
            if (builder.buildFor(reportType) {
                //don't pass components - if there's a requirement 
                //for a strongly typed subclass of Component, just 
                //let the Report instance figure it out.
                return builder.buildReport();
            }
        }
    }
}


//example use
public class PDFReportBuilder implements ReportBuilder {

    ComponentSource componentSource;

    @Override
    public Report buildReport() {
         PDFReport report;

         for (PDFComponent component : componentSource.getPDFComponents()) {
            // assemble report ... 
            report.includeComponent(component);
            // no instanceof operations!
        }

        return report;
    }
}
2 голосов
/ 12 сентября 2010

Это работает, если вы превращаете тип Component в переменную типа для ReportBuilder:

public interface ReportBuilder<T extends Component> {
    public Report buildReport(List<T> components);
}

public class PDFReportBuilder implements ReportBuilder<PDFComponent> {
    public Report buildReport(List<PDFComponent> components);
}

Вам придется оценить, действительно ли вам нужна переменная типа в ReportBuilder. Это не всегда правильный выбор. Кроме того, если вы также хотите, чтобы PDFReportBuilder.buildReport имел тип возвращаемого значения, равный PDFReport, то вам также нужно иметь его в качестве переменной типа (то есть public interface ReportBuilder<T extends Component, S extends Report>).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...