【问题标题】:Java best design for an object that can be different, but do the sameJava 对可以不同但做相同的对象的最佳设计
【发布时间】:2017-04-15 17:03:49
【问题描述】:

基本上我需要有一个灵活的设计来处理。我在想这样的事情:

  • 拥有一个接口DataProcessor,然后拥有不同类型的具体类,例如CSVDataProcessor,FTPDataProcessor,CloudDataProcessor
  • 所有 DataProcessor 类都将实现在 DataProcessor 接口上定义的方法 process()

我的问题是:

如果每个具体类需要不同的参数来执行process() 方法,那么最好的设计应该是什么。例如,FTPDataProcessor 需要文件、主机、端口等参数 - CSVDataProcessor 需要文件位置,架构 - CloudDataProcessor 需要端点服务、用户、密码??

我的第一个想法是,使用构造函数将具体要求传递给每个具体的类,但是如果需要的参数很多怎么办?

未来会添加更多DataProcessor的具体实现,这就是为什么我想从一开始就设计一个非常灵活的设计来实现它。

【问题讨论】:

  • 我希望我的答案是你想要的?
  • 正如您所提到的,构造函数必须采用适当的参数,这些参数可以存储在实例变量中。但是如果有很多参数,实现一个builder来一个一个地接受它们并构造对象。您可以改为实现一个可以创建和返回对象的 DataProcessorFactory。没有单一的“最佳”方法,每种方法都有一定的优点。
  • 每个应用程序只需要一个DataProcessor 还是多个?

标签: java object design-patterns interface


【解决方案1】:

工厂设计模式

是您问题的答案。

DataProcessor p = ProcessorsFactory.getProcessor(yourParametersHere);

您应该重载您的工厂getProcessor() 进行繁重的工作。最后,您的对象将只知道 DataProcessor 接口和工厂,而不知道实现。这就是领先技术之一的工作原理: Spring Bean 工厂示例 http://javajadhav.blogspot.bg/2013/07/03-understanding-spring-bean-factory.html 所以你可以创建这样的东西。在ProcessorsFactory 中,您可以从您的应用程序加载一些配置文件,并根据写入的属性来实例化适当的处理器实现。最后,您将只使用您的 interface.process + 配置文件。

【讨论】:

  • 为什么会遭到反对?这是一个很好的答案!您不能在 Java 中定义没有参数的接口,然后在实现中的方法上有一些参数。这将违反替代原则。
【解决方案2】:

一种可能的解决方案是:

interface DataProcessorParameters {}

class FtpParams implements DataProcessorParameters {/* connection params come here*/}

interface DataProcessor<T extends DataProcessorParameters> {
    void process(T params);
}

class FtpProcessor implements DataProcessor<FtpParams> {
    void process(FtpParams params) {
       //...do stuff
    }
}

虽然这可行,但您必须首先询问为什么需要接口,因为调用者必须知道实际类型才能传递正确的参数。

FtpProcessor proc = new FtpProcessor();
proc.process(new FtpParams()); // this works

DataProcessor proc = ... //let's say this came from somewhere else, and all we know about it is that it's a data processor
proc.process(???); //this doesn't

您还提到了在构造函数中传递参数,您可以这样做,但这在语义上有所不同:在这种情况下,处理器实例将不得不在其整个生命周期中使用相同的参数,本质上它们将是“一枪”。如果您在process() 方法中传递参数,您的处理器将是“可重用的”。

一个不一定比另一个更好,因此请根据更适合您的设计的内容做出选择,请注意差异。

【讨论】:

    【解决方案3】:

    首先,我要感谢所有花时间回答我的人。我想出了一个可能的选择,我想分享并要求 cmets 看看是否是一个好的选择。

    如果我有这样的主界面:

    public interface DataProcessor {
        public void process();
    }
    

    如果我有另一个接口用于特定类型的 DataProcessors,像这样:

    public interface FTPDataProcessor extends DataProcessor {
    
        //add all parameters getter and setters but also 'with' methods
    
        public String getHost();
        public void setHost(String host);
        public FTPDataProcessor withHost(String host);
    
        public String getUsername();
        public void setUsername(String username);
        public FTPDataProcessor withUsername(String username);
    
    }
    

    然后我可以在一个 Abstract 类中初始化所有 get'ters、set'ters 和 with'ters 方法,如下所示:

    public abstract class AbstractFTPDataProcessor implements FTPDataProcessor {
    
        private String host;
        private String username;
    
        public String getHost() {
            return this.host;
        }
    
        public void setHost(String host) {
            this.host = host;
        }
    
        public FTPDataProcessor withHost(String host) {
            setHost(host);
            return this;
        }
    
        public String getUsername() {
            return this.username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public FTPDataProcessor withUsername(String username) {
            setUsername(username);
            return this;
        }
    
    }
    

    那么我的具体类可以只实现方法process(),像这样:

    public class SimpleFTPDataProcessor extends AbstractFTPDataProcessor {
    
        @Override
        public void process() {
            //do some stuff
        }
    
    }
    

    终于可以在我的应用中使用它了,像这样:

    public class Application {
    
        public static void main(String[] args) {
    
            DataProcessor processor = new SimpleFTPDataProcessor()
                    .withHost('host')
                    .withUsername('username');
    
            processor.process();
    
        }
    
    }
    

    其他人如何看待这种可能的选择?这是一个好的做法吗?有什么优点和/或缺点?

    感谢大家的反馈。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-13
      相关资源
      最近更新 更多