Java Bulider设计模式,用于为具有大量参数的方法生成通用方法
我有一个接口Java Bulider设计模式,用于为具有大量参数的方法生成通用方法,java,design-patterns,Java,Design Patterns,我有一个接口Itest,ClassA&ClassB正在实现这个接口testA和testB分别是这些类中的方法 testA(String a, String b, String c, D d, E e) testB(String a, String b, String c, F f, G g) 这里的D,E,F,G是自定义数据类型(与数据库相关)。我简化了方法,实际上它们有更多的参数 我需要在Itest接口的testAB中创建一个通用方法,并在这两个类中实现它,而不是使用它们自己的方法 te
Itest
,ClassA
&ClassB
正在实现这个接口testA
和testB
分别是这些类中的方法
testA(String a, String b, String c, D d, E e)
testB(String a, String b, String c, F f, G g)
这里的D
,E
,F
,G
是自定义数据类型(与数据库相关)。我简化了方法,实际上它们有更多的参数
我需要在Itest
接口的testAB
中创建一个通用方法,并在这两个类中实现它,而不是使用它们自己的方法
testAB(String a, String b, String c, D d, E e, F f, G g)
随着参数的数量越来越多,通用方法testAB
对于用户来说将是一件痛苦的事情,因为他必须传递如此多的null
值
- 这是否是Bulider设计模式的一个用例
- 如果是,如何使用此设计模式实现这一点
- 是的,在这里您可以使用生成器模式。
您希望将包含信息的对象传递给您的方法
这些对象可以使用内部生成器创建,而字段可以保存默认值。这里是一个小例子,它看起来是什么样子
ParamTestA<D, E> paramA = new ParamTestA<>.Builder(a, b, c).setD(d).setE(e).build();
testA(paramA);
parametasparama=newparametasa.Builder(a,b,c).setD(d).setE(e).build();
testA(paramA);
感觉你好像在做错事:
首先,您需要一个在接口中包含大量泛型参数的方法
interface ITest<D,E,F,G> {
void test(String a, D d, E e, F f, G g)
}
接口测试{
无效测试(字符串a、D、E、F、G)
}
这是错误的,因为您的接口与实现细节紧密耦合
如果您试图从方法参数的差异中提取
interface ITest {
void test(String a, Map<String, Object> params);
}
接口测试{
无效测试(字符串a,映射参数);
}
您将得到您想要的,但您将失去通用类型检查
无论如何,我将推荐您使用此变体,因为您负责将参数传递给您的方法。通过生成器模式传递参数对象。 首先也是最重要的一点,构建器模式是一种实例工厂,在调用
.build()
或类似于构建器实例的东西后,您将得到一个简单的POJO
因此,生成器通常遵循这种语法:
SomeClass instance = new SomeClass.Builder<>(requiredArgument).optionalArgumentX(x).build();
这个抽象类具有在您的示例中找到的所有公共参数(a
、b
和c
),以及一个额外的可选参数z
,其类型可以一般地传递。除了抽象的定义外,大部分内容都应该是直截了当的。泛型构建器类型的定义是这样的,我们可以通过子构建器实际创建适当的子类
子类(包括子生成器)现在可以如下所示:
public class TestParamA<D,E,Z> extends TestParam<Z>
{
public static class Builder<T extends TestParamA<D,E,Z>, B extends TestParamA.Builder<? extends TestParamA<D,E,Z>, ? extends B, D,E,Z>, D,E,Z> extends TestParam.CommonBuilder<TestParamA.Builder<?,?, D,E,Z>, Z>
{
protected D d;
protected E e;
public Builder(String a, String b, String c)
{
super(a, b, c);
}
public B withD(D d)
{
this.d = d;
return (B)this;
}
public B withE(E e)
{
this.e = e;
return (B)this;
}
@Override
public <T> T build()
{
TestParamA t = new TestParamA("TestParamA", a, b, c, z, d, e);
return (T)t;
}
}
protected final D d;
protected final E e;
protected TestParamA(String name, String a, String b, String c, Z z, D d, E e)
{
super(name, a, b, c, z);
this.d = d;
this.e = e;
}
public D getD()
{
return d;
}
public E getE()
{
return e;
}
@Override
protected String getContent()
{
return ", D: " + d + ", E: " + e;
}
}
运行此测试类时,应返回以下输出:
Test for ParamA: TestParamA[A: a, B: b, C: c, D: D, E: E]
Test for ParamB: TestParamB[A: a, B: b, C: c, Z: z, F: F, G: G]
Test for Param: TestParamA[A: a, B: b, C: c, Z: z, D: D, E: E]
Test for Param: TestParamB[A: a, B: b, C: c, F: F, G: G]
Test for BuilderA: TestParamA[A: a, B: b, C: c, D: D, E: E]
Test for BuilderB: TestParamB[A: a, B: b, C: c, Z: z, F: F, G: G]
Test for CommonBuilder: TestParamA[A: a, B: b, C: c, Z: z, D: D, E: E]
Test for CommonBuilder: TestParamB[A: a, B: b, C: c, F: F, G: G]
newd()
和使用new
创建的其他类只是简单的POJO,它们在toString()中返回它们的简单类名
可以看出,每个调用的测试方法都包含通过相应的生成器创建的相应子参数对象。对于更通用的方法,如test(TestParam TestParam)
或testCommon(…)
,您可能需要将参数对象强制转换为具体类,然后才能实际访问这些具体类特有的方法(getD()
,…),但我想您对这个概念还是很熟悉的
缺点
- 与传统的构造函数调用相比,编写生成器会产生额外的开销
- 创建一个新实例还需要额外的字符输入成本
专业人士
- 灵活的参数顺序是可能的。通常您不必记住参数的顺序,如果您处理的参数超过5个,这是非常好的。但是,所需的参数通常在生成器的构造函数中指定,因此需要固定的顺序,除非可以使用生成器方法指定它们
- 支持相关参数的分组(如
。维度(int x,int y,int width,int height)
)
- 类型安全
- 可扩展性(如本文所示)
- 生成的类型可以用作
参数对象
,因此如果创建的对象遵循父子结构,则依赖于多态性
- 增加可读性支持。尽管在这篇文章的评论中有争论,但如果您在一个月后返回代码,并且必须记住传递的所有参数是什么,那么构建器将提高可读性。构建器向参数添加某种词汇语义。因此,通过适当地构造fluent方法调用,可以提高可读性
何时(不)使用生成器
这就是说,建设者很好,但也带来了开销。如果只有很少的参数或应创建许多不同的独立类型,则不应使用它们,因为需要为每个类型设置生成器。在这里,对于第一种情况,简单的POJO实例化和对于后一种情况的一般工厂模式是最好的
如果您的方法需要尽可能灵活,并且不需要依赖类型安全或提供某些内部类型提取机制(如Camel的类型转换器),请使用Map
作为参数对象。Camel将此方法用于其消息头。Activiti BPMN引擎也使用这种方法。(由亚当斯基沃克在本帖中解释)
如果场景数量有限,参数数量明确,请使用简单方法重载(如Chetan Kinger所述)
如果您一直难以记住参数的确切顺序,那么将来可能会有某种类型的类扩展,或者如果您有一堆可选参数(甚至可能是
Builder<T extends TestParamA<D,E,Z>,
B extends TestParamA.Builder<? extends TestParamA<D,E,Z>, ? extends B, D,E,Z>,
D,E,Z>
extends TestParam.CommonBuilder<TestParamA.Builder<?,?, D,E,Z>, Z>
public class Main
{
public static void main(String ... args)
{
TestParamA<D,E,?> a = new TestParamA.Builder<>("a","b","c").withD(new D()).withE(new E()).build();
TestParamB<F,G,String> b = new TestParamB.Builder<>("a","b","c").withF(new F()).withG(new G()).withOptionalZ("z").build();
TestParam<String> c = new TestParamA.Builder<>("a","b","c").withD(new D()).withE(new E()).withOptionalZ("z").build();
TestParam d = new TestParamB.Builder<>("a","b","c").withF(new F()).withG(new G()).build();
test(a);
test(b);
test(c);
test(d);
test(new TestParamA.Builder<>("a","b","c").withD(new D()).withE(new E()));
test(new TestParamB.Builder<>("a","b","c").withF(new F()).withG(new G()).withOptionalZ("z"));
testCommon(new TestParamA.Builder<>("a","b","c").withD(new D()).withE(new E()).withOptionalZ("z"));
testCommon(new TestParamB.Builder<>("a","b","c").withF(new F()).withG(new G()));
}
public static void test(TestParamA<?,?,?> testParam)
{
System.out.println("Test for ParamA: " + testParam.toString());
}
public static void test(TestParamB<?,?,?> testParam)
{
System.out.println("Test for ParamB: " + testParam.toString());
}
public static void test(TestParam<?> testParam)
{
System.out.println("Test for Param: " + testParam.toString());
}
public static void test(TestParamA.Builder<?,?,?,?,?> builder)
{
System.out.println("Test for BuilderA: " + builder.build().toString());
}
public static void test(TestParamB.Builder<?,?,?,?,?> builder)
{
System.out.println("Test for BuilderB: " + builder.build().toString());
}
public static void testCommon(TestParam.CommonBuilder<?,?> builder)
{
System.out.println("Test for CommonBuilder: " + builder.build().toString());
}
}
Test for ParamA: TestParamA[A: a, B: b, C: c, D: D, E: E]
Test for ParamB: TestParamB[A: a, B: b, C: c, Z: z, F: F, G: G]
Test for Param: TestParamA[A: a, B: b, C: c, Z: z, D: D, E: E]
Test for Param: TestParamB[A: a, B: b, C: c, F: F, G: G]
Test for BuilderA: TestParamA[A: a, B: b, C: c, D: D, E: E]
Test for BuilderB: TestParamB[A: a, B: b, C: c, Z: z, F: F, G: G]
Test for CommonBuilder: TestParamA[A: a, B: b, C: c, Z: z, D: D, E: E]
Test for CommonBuilder: TestParamB[A: a, B: b, C: c, F: F, G: G]
public interface ITest {
public void test(String a,String b,String c,D d,E e,F f,G g);
}
public class A implements ITest {
//this is an overload - v1
public void test(String a,String b,String c,D d,E e) {
//dispatch the call to the overriden method
test(a,b,c,d,e,null,null);
}
//this is an overload - v2
public void test(String a,String b,String c,E e,F f) {
//dispatch the call to the overriden method
test(a,b,c,null,null,e,f);
}
@Override
//this is an overriden method - v3
public void test(String a,String b,String c,D d,E e,F f,G g) {
if(d!=null && e!=null) {
//use a,b,c,d,e and do something
}
if(f!=null && g!=null) {
//use a,b,c,f,g and do something
}
}
}
classAObj.test("1","2","3",new D(),new E());//calls overloaded method - v1
classAObj.test("1","2","3",new F(),new G());//calls overloaded method - v2
classAObj.test("1","2","3",new D(),new E(),new F(),new G());//calls overriden method - v3