In my client I would like to specify strategies to be used in the class produced by builder.
However, I can't pass these strategy objects to builder, because their initialization is partially handled by builder. Yet, I still have to communicate builder which object to use and which additional parameters to pass it.
Here's Builder class
public class MarketGeneratorBuilder {
private MarketGenerator.Parameters parameters;
public MarketGeneratorBuilder(MarketGenerator.Parameters parameters) {
this.parameters = parameters;
}
public MarketGenerator build() {
return new MarketGenerator(
parameters,
new GoodsGenerator(
new UniformDistribution(
new ValueRange(0,parameters.getNumberOfLevels()-1)
)
),
new ITGenerator(),
new OTGenerator(),
new IOTGenerator(
new UniformDistribution(
new ValueRange(1,parameters.getNumberOfLevels()-2)
),
new BundlesGenerator(
new ForwardMarkovDistribution(
new MarkovDistribution.Parameters(
new ValueRange(0,parameters.getNumberOfLevels()-1),
0.1,
0.1
)
),
new UniformDistribution(
new ValueRange(1,parameters.getNumberOfGoodsToCreate()-1)
)
),
new BundlesGenerator(
new BackwardMarkovDistribution(
new MarkovDistribution.Parameters(
new ValueRange(0,parameters.getNumberOfLevels()-1),
0.2,
0.2
)
),
new UniformDistribution(
new ValueRange(1,parameters.getNumberOfGoodsToCreate()-1)
)
)
)
);
}
}
Distributions (UniformDistribution, ForwardMarkovDistribution, ForwardMarkovDistribution and potentially more to come) are hardcoded now, but should be chosen by client. The value range is defined by Builder (the ValueRange objects). Yet, some distributions take additional parameters (ForwardMarkovDistribution takes ValueRange, alpha, beta), which should be defined by client.
The only solution I can see is to pull ValueRange from constructor into setter. But it seems wrong as its an essencial field for the object. Also, I would like client code to not contain internal logic of the builder. More like,
DistributionType levelDistribution = new DistributionType(Distributions.UNIFORM);
DistributionType goodsDistribution = new DistributionsType(Distributions.MARKOV_FORWARD, 0.1,0.1);
But in this case I dont understand how to enforce data integrity. Meaning that when the client chooses Distributions.UNIFORM there are no more parameters to pass. Or in case of Distributions.MARKOV_FORWARD he has to pass alpha and beta.
So, I was hoping you could point me to a better pattern. Thanks!
For full flexibility, add a provider/factory for each of the distributions:
interface DistributionProvider<D extends Distribution> {
D create(ValueRange vr);
}
class UniformDistributionProvider implements DistributionProvider<UniformDistribution> {
UniformDistribution create(ValueRange vr) {
return new UniformDistribution(vr);
}
}
class ForwardMarkovDistributionProvider implements DistributionProvider<ForwardMarkovDistribution> {
private final MarkovDistribution.Parameters params;
ForwardMarkovDistributionProvider(MarkovDistribution.Parameters pParams) {
params = pParams;
}
ForwardMarkovDistribution create(ValueRange vr) {
return new ForwardMarkovDistribution(vr, params);
}
}
// etc.
(Remove the ValueRange from MarkovDistribution.Parameters.)
Basically, each of the providers stores all necessary parameters for the distribution, except the ValueRange.
Then clients can instantiate the provider for the distribution they want, configuring it with the appropriate values.
The builder would get those instances of DistributionProvider and use these to create the distributions, passing a ValueRange.
I see a couple of different patterns here.
You could have a couple of different build methods. buildUniform() and buildMarkov(float alpha, float beta).
You could have a couple of different builder classes: MarketGeneratorMarkovBuilder. They could have a base class with a protected build method. [Not sure I like this one]
You could add the alpha and beta values to the parameters class and throw an exception if they aren't available for the MARKOV_FORWARD and throw exception if they are available for UNIFORM.
If the ValueRange is constant for the entire builder then I see no reason why, as you mention, you couldn't add it to the parameters or put a setter on the builder to inject it.
You might consider using dependency injection (in Spring fashion) for all of the constant sub classes. The builder could have setters for all of them instead of being hard coded.
Hope this helps.
Related
I am trying to unit test a Java class with a method containing a lambda function. I am using Groovy and Spock for the test. For proprietary reasons I can't show the original code.
The Java method looks like this:
class ExampleClass {
AsyncHandler asynHandler;
Component componet;
Component getComponent() {
return component;
}
void exampleMethod(String input) {
byte[] data = input.getBytes();
getComponent().doCall(builder ->
builder
.setName(name)
.data(data)
.build()).whenCompleteAsync(asyncHandler);
}
}
Where component#doCall has the following signature:
CompletableFuture<Response> doCall(Consumer<Request> request) {
// do some stuff
}
The groovy test looks like this:
class Spec extends Specification {
def mockComponent = Mock(Component)
#Subject
def sut = new TestableExampleClass(mockComponent)
def 'a test'() {
when:
sut.exampleMethod('teststring')
then:
1 * componentMock.doCall(_ as Consumer<Request>) >> { args ->
assert args[0].args$2.asUtf8String() == 'teststring'
return new CompletableFuture()
}
}
class TestableExampleClass extends ExampleClass {
def component
TestableExampleClass(Component component) {
this.component = component;
}
#Override
getComponent() {
return component
}
}
}
The captured argument, args, shows up as follows in the debug window if I place a breakpoint on the assert line:
args = {Arrays$ArrayList#1234} size = 1
> 0 = {Component$lambda}
> args$1 = {TestableExampleClass}
> args$2 = {bytes[]}
There are two points confusing me:
When I try to cast the captured argument args[0] as either ExampleClass or TestableExampleClass it throws a GroovyCastException. I believe this is because it is expecting Component$Lambda, but I am not sure how to cast this.
Accessing the data property using args[0].args$2, doesn't seem like a clean way to do it. This is likely linked to the casting issue mentioned above. But is there a better way to do this, such as with args[0].data?
Even if direct answers can't be given, a pointer to some documentation or article would be helpful. My search results discussed Groovy closures and Java lambdas comparisons separately, but not about using lambdas in closures.
Why you should not do what you are trying
This invasive kind of testing is a nightmare! Sorry for my strong wording, but I want to make it clear that you should not over-specify tests like this, asserting on private final fields of lambda expressions. Why would it even be important what goes into the lambda? Simply verify the result. In order to do a verification like this, you
need to know internals of how lambdas are implemented in Java,
those implementation details have to stay unchanged across Java versions and
the implementations even have to be the same across JVM types like Oracle Hotspot, OpenJ9 etc.
Otherwise, your tests break quickly. And why would you care how a method internally computes its result? A method should be tested like a black box, only in rare cases should you use interaction testing,where it is absolutely crucial in order to make sure that certain interactions between objects occur in a certain way (e.g. in order to verify a publish-subscribe design pattern).
How you can do it anyway (dont!!!)
Having said all that, just assuming for a minute that it does actually make sense to test like that (which it really does not!), a hint: Instead of accessing the field args$2, you can also access the declared field with index 1. Accessing by name is also possible, of course. anyway, you have to reflect on the lambda's class, get the declared field(s) you are interested in, make them accessible (remember, they are private final) and then assert on their respective contents. You could also filter by field type in order to be less sensitive to their order (not shown here).
Besides, I do not understand why you create a TestableExampleClass instead of using the original.
In this example, I am using explicit types instead of just def in order to make it easier to understand what the code does:
then:
1 * mockComponent.doCall(_ as Consumer<Request>) >> { args ->
Consumer<Request> requestConsumer = args[0]
Field nameField = requestConsumer.class.declaredFields[1]
// Field nameField = requestConsumer.class.getDeclaredField('arg$2')
nameField.accessible = true
byte[] nameBytes = nameField.get(requestConsumer)
assert new String(nameBytes, Charset.forName("UTF-8")) == 'teststring'
return new CompletableFuture()
}
Or, in order to avoid the explicit assert in favour of a Spock-style condition:
def 'a test'() {
given:
String name
when:
sut.exampleMethod('teststring')
then:
1 * mockComponent.doCall(_ as Consumer<Request>) >> { args ->
Consumer<Request> requestConsumer = args[0]
Field nameField = requestConsumer.class.declaredFields[1]
// Field nameField = requestConsumer.class.getDeclaredField('arg$2')
nameField.accessible = true
byte[] nameBytes = nameField.get(requestConsumer)
name = new String(nameBytes, Charset.forName("UTF-8"))
return new CompletableFuture()
}
name == 'teststring'
}
I am sorry for the vague question. I am not sure what I'm looking for here.
I have a Java class, let's call it Bar. In that class is an instance variable, let's call it foo. foo is a String.
foo cannot just have any value. There is a long list of strings, and foo must be one of them.
Then, for each of those strings in the list I would like the possibility to set some extra conditions as to whether that specific foo can belong in that specific type of Bar (depending on other instance variables in that same Bar).
What approach should I take here? Obviously, I could put the list of strings in a static class somewhere and upon calling setFoo(String s) check whether s is in that list. But that would not allow me to check for extra conditions - or I would need to put all that logic for every value of foo in the same method, which would get ugly quickly.
Is the solution to make several hundred classes for every possible value of foo and insert in each the respective (often trivial) logic to determine what types of Bar it fits? That doesn't sound right either.
What approach should I take here?
Here's a more concrete example, to make it more clear what I am looking for. Say there is a Furniture class, with a variable material, which can be lots of things, anything from mahogany to plywood. But there is another variable, upholstery, and you can make furniture containing cotton of plywood but not oak; satin furniture of oak but not walnut; other types of fabric go well with any material; et cetera.
I wouldn't suggest creating multiple classes/templates for such a big use case. This is very opinion based but I'll take a shot at answering as best as I can.
In such a case where your options can be numerous and you want to keep a maintainable code base, the best solution is to separate the values and the logic. I recommend that you store your foo values in a database. At the same time, keep your client code as clean and small as possible. So that it doesn't need to filter through the data to figure out which data is valid. You want to minimize dependency to data in your code. Think of it this way: tomorrow you might need to add a new material to your material list. Do you want to modify all your code for that? Or do you want to just add it to your database and everything magically works? Obviously the latter is a better option. Here is an example on how to design such a system. Of course, this can vary based on your use case or variables but it is a good guideline. The basic rule of thumb is: your code should have as little dependency to data as possible.
Let's say you want to create a Bar which has to have a certain foo. In this case, I would create a database for BARS which contains all the possible Bars. Example:
ID NAME FOO
1 Door 1,4,10
I will also create a database FOOS which contains the details of each foo. For example:
ID NAME PROPERTY1 PROPERTY2 ...
1 Oak Brown Soft
When you create a Bar:
Bar door = new Bar(Bar.DOOR);
in the constructor you would go to the BARS table and query the foos. Then you would query the FOOS table and load all the material and assign them to the field inside your new object.
This way whenever you create a Bar the material can be changed and loaded from DB without changing any code. You can add as many types of Bar as you can and change material properties as you goo. Your client code however doesn't change much.
You might ask why do we create a database for FOOS and refer to it's ids in the BARS table? This way, you can modify the properties of each foo as much as you want. Also you can share foos between Bars and vice versa but you only need to change the db once. cross referencing becomes a breeze. I hope this example explains the idea clearly.
You say:
Is the solution to make several hundred classes for every possible
value of foo and insert in each the respective (often trivial) logic
to determine what types of Bar it fits? That doesn't sound right
either.
Why not have separate classes for each type of Foo? Unless you need to define new types of Foo without changing the code you can model them as plain Java classes. You can go with enums as well but it does not really give you any advantage since you still need to update the enum when adding a new type of Foo.
In any case here is type safe approach that guarantees compile time checking of your rules:
public static interface Material{}
public static interface Upholstery{}
public static class Oak implements Material{}
public static class Plywood implements Material{}
public static class Cotton implements Upholstery{}
public static class Satin implements Upholstery{}
public static class Furniture<M extends Material, U extends Upholstery>{
private M matrerial = null;
private U upholstery = null;
public Furniture(M matrerial, U upholstery){
this.matrerial = matrerial;
this.upholstery = upholstery;
}
public M getMatrerial() {
return matrerial;
}
public U getUpholstery() {
return upholstery;
}
}
public static Furniture<Plywood, Cotton> cottonFurnitureWithPlywood(Plywood plywood, Cotton cotton){
return new Furniture<>(plywood, cotton);
}
public static Furniture<Oak, Satin> satinFurnitureWithOak(Oak oak, Satin satin){
return new Furniture<>(oak, satin);
}
It depends on what you really want to achieve. Creating objects and passing them around will not magically solve your domain-specific problems.
If you cannot think of any real behavior to add to your objects (except the validation), then it might make more sense to just store your data and read them into memory whenever you want. Even treat rules as data.
Here is an example:
public class Furniture {
String name;
Material material;
Upholstery upholstery;
//getters, setters, other behavior
public Furniture(String name, Material m, Upholstery u) {
//Read rule files from memory or disk and do all the checks
//Do not instantiate if validation does not pass
this.name = name;
material = m;
upholstery = u;
}
}
To specify rules, you will then create three plain text files (e.g. using csv format). File 1 will contain valid values for material, file 2 will contain valid values for upholstery, and file 3 will have a matrix format like the following:
upholstery\material plywood mahogany oak
cotton 1 0 1
satin 0 1 0
to check if a material goes with an upholstery or not, just check the corresponding row and column.
Alternatively, if you have lots of data, you can opt for a database system along with an ORM. Rule tables then can be join tables and come with extra nice features a DBMS may provide (like easy checking for duplicate values). The validation table could look something like:
MaterialID UpholsteryID Compatability_Score
plywood cotton 1
oak satin 0
The advantage of using this approach is that you quickly get a working application and you can decide what to do as you add new behavior to your application. And even if it gets way more complex in the future (new rules, new data types, etc) you can use something like the repository pattern to keep your data and business logic decoupled.
Notes about Enums:
Although the solution suggested by #Igwe Kalu solves the specific case described in the question, it is not scalable. What if you want to find what material goes with a given upholstery (the reverse case)? You will need to create another enum which does not add anything meaningful to the program, or add complex logic to your application.
This is a more detailed description of the idea I threw out there in the comment:
Keep Furniture a POJO, i.e., just hold the data, no behavior or rules implemented in it.
Implement the rules in separate classes, something along the lines of:
interface FurnitureRule {
void validate(Furniture furniture) throws FurnitureRuleException;
}
class ValidMaterialRule implements FurnitureRule {
// this you can load in whatever way suitable in your architecture -
// from enums, DB, an XML file, a JSON file, or inject via Spring, etc.
private Set<String> validMaterialNames;
#Overload
void validate(Furniture furniture) throws FurnitureRuleException {
if (!validMaterialNames.contains(furniture.getMaterial()))
throws new FurnitureRuleException("Invalid material " + furniture.getMaterial());
}
}
class UpholsteryRule implements FurnitureRule {
// Again however suitable to implement/config this
private Map<String, Set<String>> validMaterialsPerUpholstery;
#Overload
void validate(Furniture furniture) throws FurnitureRuleException {
Set<String> validMaterialNames = validMaterialsPerUpholstery.get(furniture.getUpholstery();
if (validMaterialNames != null && !validMaterialNames.contains(furniture.getMaterial()))
throws new FurnitureRuleException("Invalid material " + furniture.getMaterial() + " for upholstery " + furniture.getUpholstery());
}
}
// and more complex rules if you need to
Then have some service along the lines of FurnitureManager. It's the "gatekeeper" for all Furniture creation/updates:
class FurnitureManager {
// configure these via e.g. Spring.
private List<FurnitureRule> rules;
public void updateFurniture(Furniture furniture) throws FurnitureRuleException {
rules.forEach(rule -> rule.validate(furniture))
// proceed to persist `furniture` in the database or whatever else you do with a valid piece of furniture.
}
}
material should be of type Enum.
public enum Material {
MAHOGANY,
TEAK,
OAK,
...
}
Furthermore you can have a validator for Furniture that contains the logic which types of Furniture make sense, and then call that validator in every method that can change the material or upholstery variable (typically only your setters).
public class Furniture {
private Material material;
private Upholstery upholstery; //Could also be String depending on your needs of course
public void setMaterial(Material material) {
if (FurnitureValidator.isValidCombination(material, this.upholstery)) {
this.material = material;
}
}
...
private static class FurnitureValidator {
private static boolean isValidCombination(Material material, Upholstery upholstery) {
switch(material) {
case MAHOGANY: return upholstery != Upholstery.COTTON;
break;
//and so on
}
}
}
}
We often are oblivious of the power inherent in enum types. The Java™ Tutorials clearly states "you should use enum types any time you need to represent a fixed set of constants."
How do you simply make the best of enum in resolving the challenge you presented? - Here goes:
public enum Material {
MAHOGANY( "satin", "velvet" ),
PLYWOOD( "leather" ),
// possibly many other materials and their matching fabrics...
OAK( "some other fabric - 0" ),
WALNUT( "some other fabric - 0", "some other fabric - 1" );
private final String[] listOfSuitingFabrics;
Material( String... fabrics ) {
this.listOfSuitingFabrics = fabrics;
}
String[] getListOfSuitingFabrics() {
return Arrays.copyOf( listOfSuitingFabrics );
}
public String toString() {
return name().substring( 0, 1 ) + name().substring( 1 );
}
}
Let's test it:
public class TestMaterial {
for ( Material material : Material.values() ) {
System.out.println( material.toString() + " go well with " + material.getListOfSuitingFabrics() );
}
}
Probably the approach I'd use (because it involves the least amount of code and it's reasonably fast) is to "flatten" the hierarchical logic into a one-dimensional Set of allowed value combinations. Then when setting one of the fields, validate that the proposed new combination is valid. I'd probably just use a Set of concatenated Strings for simplicity. For the example you give above, something like this:
class Furniture {
private String wood;
private String upholstery;
/**
* Set of all acceptable values, with each combination as a String.
* Example value: "plywood:cotton"
*/
private static final Set<String> allowed = new HashSet<>();
/**
* Load allowed values in initializer.
*
* TODO: load allowed values from DB or config file
* instead of hard-wiring.
*/
static {
allowed.add("plywood:cotton");
...
}
public void setWood(String wood) {
if (!allowed.contains(wood + ":" + this.upholstery)) {
throw new IllegalArgumentException("bad combination of materials!");
}
this.wood = wood;
}
public void setUpholstery(String upholstery) {
if (!allowed.contains(this.wood + ":" + upholstery)) {
throw new IllegalArgumentException("bad combination of materials!");
}
this.upholstery = upholstery;
}
public void setMaterials(String wood, String upholstery) {
if (!allowed.contains(wood + ":" + upholstery)) {
throw new IllegalArgumentException("bad combination of materials!");
}
this.wood = wood;
this.upholstery = upholstery;
}
// getters
...
}
The disadvantage of this approach compared to other answers is that there is no compile-time type checking. For example, if you try to set the wood to plywoo instead of plywood you won’t know about your error until runtime. In practice this disadvantage is negligible since presumably the options will be chosen by a user through a UI (or through some other means), so you won’t know what they are until runtime anyway. Plus the big advantage is that the code will never have to be changed so long as you’re willing to maintain a list of allowed combinations externally. As someone with 30 years of development experience, take my word for it that this approach is far more maintainable.
With the above code, you'll need to use setMaterials before using setWood or setUpholstery, since the other field will still be null and therefore not an allowed combination. You can initialize the class's fields with default materials to avoid this if you want.
This is the original method:
#GET
#Produces({"application/json"})
public Response getTermClouds(#Context SecurityContext secCtxt, #Context UriInfo ui)
{
return null
}
I want to copy this method but add a new String parameter,and the annotations of the new method is the same as before,like this:
#GET
#Produces({"application/json"})
public Response getTermClouds(#Context SecurityContext secCtxt, #Context UriInfo ui,String newParam)
{
return null
}
I use javassist to do it,i don`t want to add a "get" annotation and then add a "produces" annotation,because there may be many other annotations which are unkown.How to do it as a common way?
When You try adding a new parameter to a method , Javassist does not allow to add an extra parameter to an existing method,Instead of doing that, a new method receiving the extra parameter as well as the other parameters is added to the same class.
A copy of the the CtMethod object can be obtained by CtNewMethod.copy().
Try this to create a copy of Your previous method. And can you explain exactly what you want to be done with annotations?
I realize this is old now, but I ran into the same problem trying to add parameters to Spring web handler methods, and I've figured it out. You need to copy the attributes of the old class to the new one. You'll also likely want to remove them from the old one to prevent possible conflicts. The code looks like this:
//Create a new method with the same name as the old one
CtMethod mNew = CtNewMethod.copy(mOrig, curClass, null);
//Copy all attributes from the old method to the new one. This includes annotations
for(Object attribute: mOrig.getMethodInfo().getAttributes()) {
m.getMethodInfo().addAttribute((AttributeInfo)attribute);
}
//Remove the method and parameter annotations from the old method
mOrig.getMethodInfo().removeAttribute(AnnotationsAttribute.visibleTag);
mOrig.getMethodInfo().removeAttribute(ParameterAnnotationsAttribute.visibleTag);
//Add the new String parameter to the new method
m.addParameter(cp.getOrNull("java.lang.String"));
//Add a new empty annotation entry for the new parameter (not sure this part is necessary for you.
//In my case, I was adding a new parameter to the beginning, so the old ones needed to be offset.
ParameterAnnotationsAttribute paa = (ParameterAnnotationsAttribute)m.getMethodInfo().getAttribute(ParameterAnnotationsAttribute.visibleTag);
Annotation[][] oldAnnos = paa.getAnnotations();
Annotation[][] newAnnos = new Annotation[oldAnnos.length + 1][];
newAnnos[oldAnnos.length] = new Annotation[] {};
System.arraycopy(oldAnnos, 0, newAnnos, 0, oldAnnos.length);
paa.setAnnotations(newAnnos);
//Rename the old method and add the new one to the class
mOrig.setName(mOrig.getName() + "_replaced");
curClass.addMethod(m);
I am using the below code to do the authorizatin checks.
PDPrincipal whoIsit = new PDPrincipal(userId,configURL);
PDPermission whatTheyWant = new PDPermission(objectSpaceName,"TbvA");
boolean haveAccess = whoIsit.implies(whatTheyWant);
However the implies method on com.tivoli.mts.PDPrincipal has been deprecated and has been replaced by implies method from the new PdPrincipal class from different package.
com.tivoli.pd.jazn.PDPrincipal
the new method is as follows.
public boolean implies(javax.security.auth.Subject subject)
the new method takes a Subject.
Can you please let me know how can I change my code to use the new method? How do i construct the Subject or can i get the Subject from somewhere?
Thanks,
Rohit
I was able to work out a solution for this hence sharing it here so that anyone else facing the same issue can use this code.
I found that the new com.tivoli.pd.jazn.PDPermission class has a method implies which takes in a PdAuthorization context and a com.tivoli.pd.jazn.PDPrincipal object which does the same authorization checks that the previous class com.tivoli.mts.PDPrincipal use to do.
Mentioned below is how the same authorization can be done. With this code you need not implement the JAAS code.
First construct the PdAuthorizationContext as shown below. Make sure to define a static PdAuthorizationContext object so that it can be reused untill you close it. Constructing PDAuthorizationContext for every authorization check is resource intensive and not recommended. close the context at the end of your logic
URL configURL = new URL("file:" + String locationToTamConfigFile);
PDAuthorizationContext pdAuthCtx = new PDAuthorizationContext(configURL);
Next Construct the new PDPrincipal and the PdPermission objects as shown below and call the implies method
com.tivoli.pd.jazn.PDPrincipal pdPrincipal = new com.tivoli.pd.jazn.PDPrincipal(pdAuthCtx,userId);
com.tivoli.pd.jazn.PDPermission pdPermission = new com.tivoli.pd.jazn.PDPermission(objectSpaceName,"TbvA");
boolean newimpliesTry = pdPermission.implies(pdAuthCtx,pdPrincipal);
Being fairly new to OO, I often feel I understand a concept until I try to move from a simplified example to actual requirements I am given. I'd appreciate any help understanding how to think about this particular problem.
I have a GUI which has a panel that defines a container and items that go in it. Right now, there are three types of containers. The containers have some properties (like size) and can contain one to three different types of items (two are optional). Once enough information is entered, I use the information to make a graph.
I implemented an Observer pattern. When the user enters information, it updates an observable, which notifies the graph that it has changed.
I'm happy so far. Now the wrinkles. My containers have a size, but sometimes it is entered explicitly and sometimes it is determined by what the container is holding. That is determined by the type of container. How the size is determined, if not entered explicitly, depends on whether one of the optional items is in the container. I'm not sure if the requirements writer just hates me or I am lacking enough OO experience, but those wrinkles are giving me fits. Right now, my observable just has variables to hold all the assorted information and I use a bunch of switch statements to handle the special cases.
I am thinking that I could use the builder pattern. The director would produce the data that was graphed. I would have a concrete builder for each type of container and I would instantiate the class with the container properties and the items inside it. I would have methods of the abstract builder class to return to the director the values needed for the graph, for example getContainerSize() and combine these to produce the actual data points. Also, the director could return null if the user had not yet entered enough data to complete a graph.
Am I getting close to a usable OO design? I'm not sure I didn't just bury the special casing a bit deeper.
One other wrinkle. One of the item types goes in all three containers. Right now, my observable keeps track of the container and items separately and the method that creates the graph decides what to ask for (the graph changes a lot as users play around with the values). How's that work if I have multiple builder patterns?
Maybe I am missing a step? The observable updates the builder of the current container then lets the graph know it should call the director to get its coordinates? Which would then also need to ask what the current container was?
All comments welcome that help me get my head around OO design or this problem in particular. The actual requirements have more special cases, but are variations on this basic theme.
Thanks for the replies. I think I am guilty of mixing two questions together. Here is an attempt to provide a minimal code example focusing on the Builder pattern. Note IE8 I see no identation, FireFox 8, I do- so sorry to anyone reading the code in IE8.
interface MyContainerBuilder
{
void setContents( MyContents contents );
Double myVolume();
Double myDensity();
}
class SmallContainerBuilder implements MyContainerBuilder
{
Double volume = null;
Double density = null;
MyContents contents = null;
public void setVolume()
{
if (contents != null)
{
volume = contents.myDensity() / 3.0;
}
}
public void setContents( MyContents contents )
{
this.contents = contents;
}
public Double myVolume()
{
if (volume == null)
setVolume();
return volume;
}
public Double myDensity()
{
return contents.myDensity();
}
}
class BigContainerBuilder implements MyContainerBuilder
{
Double volume = null;
Double density = null;
MyContents contents = null;
public void setVolume( Double volume )
{
this.volume = volume;
}
public void setContents( MyContents contents )
{
this.contents = contents;
}
public Double myVolume()
{
return volume;
}
public Double myDensity()
{
return contents.myDensity();
}
}
class ContainerDirector
{
Double myResult( MyContainerBuilder container )
{
return container.myVolume() * container.myDensity();
}
}
class MyContents
{
Double density;
MyContents( Double density )
{
this.density = density;
}
public Double myDensity()
{
return density;
}
}
class Test
{
public static void main(String[] args)
{
SmallContainerBuilder smallContainer = new SmallContainerBuilder();
BigContainerBuilder bigContainer = new BigContainerBuilder();
ContainerDirector director = new ContainerDirector();
//
// Assume this comes from the GUI, where an ActionListener knows which Builder
// to use based on the user's action. I'd be having my observable store this.
Double density = 15.0;
MyContents contents = new MyContents( density );
smallContainer.setContents( contents );
//
// Then I would need to tell my observer to do this.
Double results = director.myResult( smallContainer );
System.out.println( "Use this result: " + results );
}
}
I have two types of containers that use a different method to calculate the volume. So let's say I have radiobuttons to select the container type and under each radiobutton a combobox of items that can go in the selected container. The ActionListener on the combobox will put the item in the right container and save it to my observable (there are lots of other things that actually get set) and it tells my observer to use the director to get an appropriate value and the observer then updates some view component of the GUI.
My containers have a size, but sometimes it is entered explicitly and sometimes it is determined by what the container is holding. That is determined by the type of container. [...] if not entered explicitly, depends on whether one of the optional items is in the container.
Sounds like you could have different subclasses of an abstract container, each implementing getContainerSize() in a different way. One for explicitly entered, one for the case with optional item and one without it.
... and I use a bunch of switch statements to handle the special cases.
Does not sound great. Replace Conditional with Polymorphism if applicable.
I am thinking that I could use the builder pattern...
I assume that you need to determine a concrete type of object (or null) based on a set of input variables. The pattern provides a way to build a complex object if it knows what type that is, but the actual problem is to decide which type. So you need conditional code at some place. That place can be a builder but it could be simple factory as well.
Right now, my observable keeps track of the container and items separately[...] observable updates the builder of the current container[...] How's that work if I have multiple builder patterns?
Not really understanding what that Observable is observing and what changes in which case are triggering what, but Observable updating a builder (or multiple) sounds strange. That's more of a gut feeling though :)
Am I getting close to a usable OO design?
If it works, yes. But I actually can't tell you if you have created a good or usable design because I still don't know the details of your problem or your design - after reading your text several times now.
Instead of adding another page of information to your question now, try to break your problem down into smaller pieces and use code snippets / images / graphs or any type of visualization to help people understand your problem and all the connections between those pieces. Just a lot of text is rather scary and a huge OO design like that is as a whole too big and too localized for SO.
Your approach seems fine but it requires IMO quite complex Objects to justify that use.
You create a MyContents instance in your GUI via the observer. That object is then wrapped in a MyContainerBuilder which is then given to a ContainerDirector which then produces a result. That is in my opinion one step too much if MyContents or the result is simple.
Also the way you set the MyContents to the MyContainerBuilder means that you can't reuse the same concrete MyContainerBuilder instance blindly. You either have to make sure that you use it sequentially or you have to construct a new one every time.
I.e this does not work
MyContents content1 = new MyContents( 5 );
MyContents content2 = new MyContents( 6 );
smallContainer.setContents( content1 );
smallContainer.setContents( content2 ); // overwriting old state
Double results1 = director.myResult( smallContainer ); // wrong result
Double results2 = director.myResult( smallContainer );
I assume that MyContents is a generic data holding object that is filled with data in several steps by the user. Once the user is happy with it, it is submitted to be build into a result. As far as I can tell, you know at that point what the result has to be.
Below is an approach using a Strategy Pattern(? - I'm bad with all those names and little differences) which I chose to plug into the MyContents directly so the MyContents object once finalized has all details how it has to be transformed into a result. That way safes one step and you don't need to create / maintain extra builder objects. MyContents is already in a way a Builder now.
interface VolumeStrategy {
Double calculateVolume(Double density);
}
class SmallVolumeStrategy implements VolumeStrategy {
public Double calculateVolume(Double density) {
return density / 3.0;
}
}
class BigVolumeStrategy implements VolumeStrategy {
public Double calculateVolume(Double density) {
return density;
}
}
class ContainerDirector {
Double myResult( MyContents container ) {
Double density = container.myDensity();
VolumeStrategy strategy = container.myStrategy();
return density * strategy.calculateVolume(density);
}
}
class MyContents {
// built via observer
Double density;
MyContents( Double density ) {
this.density = density;
}
public Double myDensity() {
return density;
}
// plugged in at the end.
VolumeStrategy strategy;
public void setStrategy(VolumeStrategy strategy) {
this.strategy = strategy;
}
public VolumeStrategy myStrategy() {
return strategy;
}
}
public class Test {
public static void main(String[] args) {
// all those can be static
VolumeStrategy smallStrategy = new SmallVolumeStrategy();
VolumeStrategy bigStratetgy = new BigVolumeStrategy();
ContainerDirector director = new ContainerDirector();
// from the GUI
Double density = 15.0;
MyContents contents = new MyContents( density );
// building this contents ...
// ... time to submit, we know what strategy to use
contents.setStrategy(smallStrategy);
// can turn contents into result without needing to know anything about it.
Double results = director.myResult( contents );
System.out.println( "Use this result: " + results );
}
}
That's a way what I think should work well for the problem I imagine you have. I can be wrong tough.