打造智能建筑商

构建API时,您应该始终考虑谁将使用它。 当API简单易用时,用户就会感到满意。 当用户满意时,每个人也都会满意。 但是出色的可用性并非总是容易实现的。 有一些模式对此有所帮助,在这篇文章中,我将重点介绍经典的构建器模式,以及如何使用步进构建器模式对其进行增强,以构建没有大脑接口,易于使用且不会出错的对象 因此,让我们开始绘制一些上下文,我们有2个域对象代表连接到某个远程或本地服务器的用户配置。 当需要远程凭据时,在本地时。


package com.marco.sbp;
public class UserConfiguration {
        private final String name;
        private ServerDetails serverDetails;

        public UserConfiguration(String name) {
                this.name = name;
        }

        public void setServerDetails(ServerDetails serverDetails) {
                this.serverDetails = serverDetails;
        }

        public String getName() {
                return name;
        }

        public ServerDetails getServerDetails() {
                return serverDetails;
        }
}
package com.marco.sbp;
public class ServerDetails {

        private final String host;
        private String user;
        private String password;

        public ServerDetails(String host) {
                this.host = host;
        }

        public void setUser(String user) {
                this.user = user;
        }

        public void setPassword(String password) {
                this.password = password;
        }

        public String getHost() {
                return host;
        }

        public String getUser() {
                return user;
        }

        public String getPassword() {
                return password;
        }
}

我们希望使用两种不同的技术(经典的构建器模式和步骤构建器模式)来抽象上述对象的构造。

经典的构建器模式非常简单,它使用诸如onLocalHost,onRemoteHost等正确命名的方法来掩盖UserConfiguration和ServerDetails的创建。

package com.marco.sbp.builder;
import com.marco.sbp.ServerDetails;
import com.marco.sbp.UserConfiguration;
public class ClassicBuilder {

        private String name;
        private String host;
        private String user;
        private String password;

        public ClassicBuilder(String name){
                this.name = name;
        }

        public ClassicBuilder onLocalHost(){
                this.host = "localhost";
                return this;
        }

        public ClassicBuilder onRemoteHost(String remoteHost){
                this.host = remoteHost;
                return this;
        }

        public ClassicBuilder credentials(String user, String password){
                this.user = user;
                this.password = password;
                return this;
        }

        public UserConfiguration build(){
                UserConfiguration userConfiguration = new UserConfiguration(name);
                ServerDetails serverDetails = new ServerDetails(host);
                serverDetails.setUser(user);
                serverDetails.setPassword(password);                    
                userConfiguration.setServerDetails(serverDetails);
                return userConfiguration;
        }
}

步骤构建器模式仍在使用智能名称来构造对象,但是仅在需要使用接口和适当的封装时才公开这些方法。

package com.marco.sbp.builder;
import com.marco.sbp.ServerDetails;
import com.marco.sbp.UserConfiguration;

/** "Step Builder" */
public class StepBuilder {
        public static NameStep newBuilder() {
                return new Steps();
        }

        private StepBuilder() {
        }

        public static interface NameStep {
                /**
                 * @param name
                 *            unique identifier for this User Configuration
                 * @return ServerStep
                 */
                ServerStep name(String name);
        }       

        public static interface ServerStep {
                /**
                 * The hostname of the server where the User Configuration file is stored will be set to "localhost".
                 * 
                 * @return BuildStep
                 */
                public BuildStep onLocalhost();

                /**
                 * The hostname of the server where the User Configuration file is stored.
                 * 
                 * @return CredentialsStep
                 */
                public CredentialsStep onRemotehost(String host);
        }

        public static interface CredentialsStep {
                /**
                 * Username required to connect to remote machine Password required to connect to remote machine
                 * 
                 * @return BuildStep
                 */
                public BuildStep credentials(String user, String password);
        }

        public static interface BuildStep {
                /**
                 * @return an instance of a UserConfiguration based on the parameters passed during the creation.
                 */
                public UserConfiguration build();
        }

        private static class Steps implements NameStep, ServerStep, CredentialsStep, BuildStep {

                private String name;
                private String host;
                private String user;
                private String password;

                public BuildStep onLocalhost() {
                        this.host = "localhost";
                        return this;
                }

                public ServerStep name(String name) {
                        this.name = name;
                        return null;
                }

                public CredentialsStep onRemotehost(String host) {
                        this.host = host;
                        return this;
                }

                public BuildStep credentials(String user, String password) {
                        this.user = user;
                        this.password = password;
                        return this;
                }

                public UserConfiguration build() {
                        UserConfiguration userConfiguration = new UserConfiguration(name);
                        ServerDetails serverDetails = new ServerDetails(host);
                        serverDetails.setUser(user);
                        serverDetails.setPassword(password);                    
                        userConfiguration.setServerDetails(serverDetails);
                        return userConfiguration;
                }

        }
}

现在让我们看一下两个构建器的用户体验。 经典构建器将使用用户配置的名称来构造,然后它将公开其所有方法,从而使用户过于自由,无法选择下一步。

打造智能建筑商

例如,一个不小心的用户可能最终将UserConfiguration设置为localhost,而无需身份验证,仍然传递用户名和密码。

这令人困惑,并且可能导致运行时异常。

打造智能建筑商

这些是用户可以最终得到的UserConfigurations的一些可能组合,其中一些是正确的,很多是错误的:

打造智能建筑商

步骤构建器有一个完全不同的故事,这里仅显示了当时的一个步骤:

打造智能建筑商

如果不需要凭据,则不会公开它们,只有在确保对象状态一致且完整时才提供build()方法:

打造智能建筑商

使用此模式只能构建2个可能的UserConfigurations,它们既有意义又对用户清楚。

打造智能建筑商

结论

步骤构建器模式不是经典Bloch 模式的替代,有时您想强迫用户在进行创建之前填充一些参数,在这种情况下,步骤构建器正在执行此工作,否则,当需要更开放的方法时比经典的建造者更适合您。

参考:从我们的JCG合作伙伴 Marco Castigliego(在“ 删除重复并修复不良名称”博客中) 构建智能构建器

翻译自: https://www.javacodegeeks.com/2013/05/building-smart-builders.html