Düzenleme: Bu sorunun teorik bir sorunu açıkladığını belirtmek isterim ve zorunlu parametreler için yapıcı bağımsız değişkenlerini kullanabilir veya API yanlış kullanılırsa bir çalışma zamanı özel durumu atabilirim. Ancak, ben bir çözüm için arıyorum değil yapıcı argümanları veya çalışma zamanı denetimini gerektirir.
Bunun Car
gibi bir arayüzünüz olduğunu düşünün :
public interface Car {
public Engine getEngine(); // required
public Transmission getTransmission(); // required
public Stereo getStereo(); // optional
}
Comments öneririm gibi, bir Car
bir olmalı Engine
ve Transmission
ancak Stereo
isteğe bağlıdır. Bu, build()
bir Car
örneğin gerçekleştirilebileceği bir Oluşturucu'nun yalnızca oluşturucu örneğine build()
bir Engine
ve Transmission
daha önce verilmiş olması durumunda bir yöntemi olması gerektiği anlamına gelir . Bu şekilde tür denetleyicisi, veya Car
olmadan örnek oluşturmaya çalışan herhangi bir kodu derlemeyi reddeder .Engine
Transmission
Bu bir Adım Oluşturucu gerektirir . Genellikle böyle bir şey uygularsınız:
public interface Car {
public Engine getEngine(); // required
public Transmission getTransmission(); // required
public Stereo getStereo(); // optional
public class Builder {
public BuilderWithEngine engine(Engine engine) {
return new BuilderWithEngine(engine);
}
}
public class BuilderWithEngine {
private Engine engine;
private BuilderWithEngine(Engine engine) {
this.engine = engine;
}
public BuilderWithEngine engine(Engine engine) {
this.engine = engine;
return this;
}
public CompleteBuilder transmission(Transmission transmission) {
return new CompleteBuilder(engine, transmission);
}
}
public class CompleteBuilder {
private Engine engine;
private Transmission transmission;
private Stereo stereo = null;
private CompleteBuilder(Engine engine, Transmission transmission) {
this.engine = engine;
this.transmission = transmission;
}
public CompleteBuilder engine(Engine engine) {
this.engine = engine;
return this;
}
public CompleteBuilder transmission(Transmission transmission) {
this.transmission = transmission;
return this;
}
public CompleteBuilder stereo(Stereo stereo) {
this.stereo = stereo;
return this;
}
public Car build() {
return new Car() {
@Override
public Engine getEngine() {
return engine;
}
@Override
public Transmission getTransmission() {
return transmission;
}
@Override
public Stereo getStereo() {
return stereo;
}
};
}
}
}
Farklı kurucu sınıflarına bir zincir vardır ( Builder
, BuilderWithEngine
, CompleteBuilder
), bu eklenti, bir sıra tüm isteğe bağlı ayarlayıcı yöntemleri içeren son sınıfı ile, birbiri ardına ayarlayıcı bir yöntem gereklidir.
Bu, bu adım oluşturucunun kullanıcılarının , yazarın zorunlu ayarlayıcıları kullanılabilir hale getirdiği sırayla sınırlı olduğu anlamına gelir . İşte olası kullanımlara bir örnek (hepsinin kesinlikle sipariş edildiğini unutmayın: engine(e)
önce, ardından transmission(t)
ve son olarak isteğe bağlı stereo(s)
).
new Builder().engine(e).transmission(t).build();
new Builder().engine(e).transmission(t).stereo(s).build();
new Builder().engine(e).engine(e).transmission(t).stereo(s).build();
new Builder().engine(e).transmission(t).engine(e).stereo(s).build();
new Builder().engine(e).transmission(t).stereo(s).engine(e).build();
new Builder().engine(e).transmission(t).transmission(t).stereo(s).build();
new Builder().engine(e).transmission(t).stereo(s).transmission(t).build();
new Builder().engine(e).transmission(t).stereo(s).stereo(s).build();
Bununla birlikte, özellikle üreticinin sadece ayarlayıcıları değil, aynı zamanda toplayıcıları varsa veya kullanıcı, oluşturucu için belirli özelliklerin kullanılabilir olacağı sırayı kontrol edemiyorsa, bunun üreticinin kullanıcısı için ideal olmadığı birçok senaryo vardır.
Bunun için düşünebildiğim tek çözüm çok kıvrımlıdır: Belirlenmiş veya henüz belirlenmemiş her zorunlu özellik kombinasyonu için, diğer zorunlu ayarlayıcıların devlet burada build()
yöntem mevcut olmalıdır, ve bu belirleyiciler her biri bir adım daha ihtiva eden için kurucu madde daha kapsamlı bir türü verir build()
yöntem.
Ben aşağıdaki kodu eklendi, ama senden bir oluşturmanıza olanak sağlayan bir FSM oluşturmak için tip sistemini kullanıyorum söyleyebiliriz Builder
ya bir dönüştürülebilir, BuilderWithEngine
ya BuilderWithTransmission
ikisi daha sonra dönüştürülebilir hangi CompleteBuilder
, hangi uygularbuild()
yöntem. Bu oluşturucu örneklerinin herhangi birinde isteğe bağlı ayarlayıcılar çağrılabilir.
public interface Car {
public Engine getEngine(); // required
public Transmission getTransmission(); // required
public Stereo getStereo(); // optional
public class Builder extends OptionalBuilder {
public BuilderWithEngine engine(Engine engine) {
return new BuilderWithEngine(engine, stereo);
}
public BuilderWithTransmission transmission(Transmission transmission) {
return new BuilderWithTransmission(transmission, stereo);
}
@Override
public Builder stereo(Stereo stereo) {
super.stereo(stereo);
return this;
}
}
public class OptionalBuilder {
protected Stereo stereo = null;
private OptionalBuilder() {}
public OptionalBuilder stereo(Stereo stereo) {
this.stereo = stereo;
return this;
}
}
public class BuilderWithEngine extends OptionalBuilder {
private Engine engine;
private BuilderWithEngine(Engine engine, Stereo stereo) {
this.engine = engine;
this.stereo = stereo;
}
public CompleteBuilder transmission(Transmission transmission) {
return new CompleteBuilder(engine, transmission, stereo);
}
public BuilderWithEngine engine(Engine engine) {
this.engine = engine;
return this;
}
@Override
public BuilderWithEngine stereo(Stereo stereo) {
super.stereo(stereo);
return this;
}
}
public class BuilderWithTransmission extends OptionalBuilder {
private Transmission transmission;
private BuilderWithTransmission(Transmission transmission, Stereo stereo) {
this.transmission = transmission;
this.stereo = stereo;
}
public CompleteBuilder engine(Engine engine) {
return new CompleteBuilder(engine, transmission, stereo);
}
public BuilderWithTransmission transmission(Transmission transmission) {
this.transmission = transmission;
return this;
}
@Override
public BuilderWithTransmission stereo(Stereo stereo) {
super.stereo(stereo);
return this;
}
}
public class CompleteBuilder extends OptionalBuilder {
private Engine engine;
private Transmission transmission;
private CompleteBuilder(Engine engine, Transmission transmission, Stereo stereo) {
this.engine = engine;
this.transmission = transmission;
this.stereo = stereo;
}
public CompleteBuilder engine(Engine engine) {
this.engine = engine;
return this;
}
public CompleteBuilder transmission(Transmission transmission) {
this.transmission = transmission;
return this;
}
@Override
public CompleteBuilder stereo(Stereo stereo) {
super.stereo(stereo);
return this;
}
public Car build() {
return new Car() {
@Override
public Engine getEngine() {
return engine;
}
@Override
public Transmission getTransmission() {
return transmission;
}
@Override
public Stereo getStereo() {
return stereo;
}
};
}
}
}
Anlayacağınız gibi, gerekli farklı oluşturucu sınıflarının sayısı O (2 ^ n) olacaktır, burada n zorunlu ayarlayıcıların sayısıdır.
Dolayısıyla sorum: Bu daha zarif bir şekilde yapılabilir mi?
(Scala da kabul edilebilir olsa da, Java ile çalışan bir cevap arıyorum)
.engine(e)
Bir inşaatçı için iki kez çağırmak ne anlama geliyor ?
build()
aradığınız değil eğer engine(e)
ve transmission(t)
daha önce.
Engine
uygulamayla başlayıp daha sonra daha spesifik bir uygulamayla üzerine yazmak isteyebilirsiniz . Ama muhtemelen engine(e)
bir pasör değil, toplayıcı olsaydı daha mantıklı olurdu addEngine(e)
. Bu Car
, birden fazla motor / motora sahip hibrid otomobiller üretebilen bir inşaatçı için yararlı olacaktır . Bu anlaşmalı bir örnek olduğu için, neden bunu yapmak isteyebileceğinizle ilgili ayrıntılara girmedim - kısaca.
this
?