Java构造函数(反模式)超类字符串

时间:2015-07-02 00:51:29

标签: java design-patterns constructor

以下设计的目的是允许String值(实际上)为子类,以便能够建立许多冲突的构造方法(例如,即使参数名称,方法签名也是相同的)不同)。

请考虑以下(非功能性)设计:

Id Interface(空标记界面)

public interface Id {}

类接口(空标记接口)

public interface Classes {}

标题界面(空标记界面)

public interface Title {}

工具类

public Tool(Id id) throws Exception
{
    this.span = new Span();

    this.span.addAttribute(new Attribute(Attribute.ID, (String)((Object) id)));
}

public Tool(Classes classes) throws Exception
{
    this.span = new Span();
    this.span.addAttribute(new Attribute(Attribute.CLASS, (String)((Object) classes)));
}

public Tool(Title title) throws Exception
{
    this.span = new Span();
    this.span.addAttribute(new Attribute(Attribute.TITLE, (String)((Object) title)));
}

public Tool(Id id, Classes classes, Title title) throws Exception
{
    this.span = new Span();
    this.span.addAttribute(new Attribute(Attribute.ID, (String)((Object) id)));
    this.span.addAttribute(new Attribute(Attribute.CLASS, (String)((Object) classes)));
    this.span.addAttribute(new Attribute(Attribute.TITLE, (String)((Object) title)));
}

public void Test() throws Exception
{
    Tool hammer = new Tool((Id)((Object)"hammer"));
    Tool poweredTool = new Tool((Classes)((Object)"tool powered"));
    Tool tool = new Tool((Id)((Object)"invention"), (Classes)((Object)"tool powered"), (Title)((Object)"define a new tool"));
}

该方法需要每个参数“type”的接口和从特定接口“type”到Object然后再转换为String的向下转换/向上转换...

我对这种方法感到不舒服,我希望有一种设计模式可以减轻我对子类String的渴望(仅用于构造方法区分的目的)......

我有一个可变方法,它采用任意的名称值对集合来提供固定参数构造函数的替代方法,但上面显示的构造函数是最常见的组合,因此为了方便程序员,他们目前正在考虑如提供......

谢谢!

4 个答案:

答案 0 :(得分:5)

考虑一下构造函数的外观,我建议你去除它们并使用Builder pattern代替:

class Tool {

    private Tool() { // Preventing direct instantiation with private constructor
        this.span = new Span();
    }

    ... // Tool class code

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private final Tool tool = new Tool();

        public Builder withId(String id) { 
            tool.span.addAttribute(new Attribute(Attribute.ID, id));
            return this;
        }   

        ... // other methods in the same manner

        public Tool build() {
            // Add some validation if necessary
            return tool;
        }
    }
}

用法:

Tool tool = Tool.builder()
                .withId("id")
                .withClasses("classA, classB")
                .build();

答案 1 :(得分:2)

你的基本问题是构造函数有一个固定的名称(类的名称),因此它们必须通过参数类型区分,因此你不能拥有多个具有相同类型但不同解释它们的构造函数。 / p>

有构建器模式(参见其他答案)或者这个 - 工厂方法模式:

// private constructor
private Tool() { }

// factory methods with same parameters but different names:
public static Tool forId(String id) throws Exception {
    Tool tool = new Tool();
    tool.span = new Span();
    tool.span.addAttribute(new Attribute(Attribute.ID, id));
    return tool;
}

public static Tool forClasses(String classes) throws Exception {
    Tool tool = new Tool();
    tool.span = new Span();
    tool.span.addAttribute(new Attribute(Attribute.CLASS, classes));
    return tool;
}

// etc for other interpretations of a String

你可以重构它以使它更清洁,但这显示了方法的本质。

答案 2 :(得分:1)

我过去使用的是这样的:

public abstract class StringValue {
    private final String value;

    protected StringValue(String value) { this.value = value; }

    public String toString() { return value; }
}

public class Id extends StringValue {
    public Id(String id) { super(id); }
}

public class Classes extends StringValue {
    public Classes(String classes) { super(classes); }
}

这样,您就可以获得真实的,面向对象的类型。如果您为每种类型都有特定的额外逻辑(验证逻辑,转换逻辑以不同方式表示与不同系统集成的值等),这一功能尤为强大。

如果您不需要额外的逻辑,那么只需要创建几行代码即可。

答案 3 :(得分:0)

最好的解决方案是使用具有不同方法名称的静态工厂方法,如波希米亚的答案所示。

鉴于无法避免超载的假设要求,我们必须提供不同的方法签名;更准确地说,在擦除下不同的签名。因此,有必要定义和使用自定义类型,如Id等。

一种方法是让方法参数仅用于重载分辨率,而不关心其值:

public Tool(Id id, String value){...}

new Tool( (Id)null, "hammer" );

使用(Id)null

的常量可能看起来更好
public static final Id ID = null;

new Tool(ID, "hammer");

如果我们要求必须在参数中携带值,则需要包装值的包装类型,如Bolwidt的答案。

在java8中,我们也可以使用lambda表达式来定义包装器:

    public interface Id extends Supplier<String>{}


    public Tool(Id id)
    {
        String attributeValue = id.get();
        ...
    }

    new Tool((Id)()->"hammer"); 

    new Tool((Id)"hammer"::toString);