I have an interface, Attribute
, that is going to be implemented in various ways,
class AttributeA implements Attribute{\\..}
class AttributeB implements Attribute{\\..}
Then I'm planning to have a class containing a list of interface members
class Example {
List<Attribute> attributes;
//..
}
And then I'm planning to have a class containing a list of those
class ExamplesAndMore {
List<Example> examples;
//..
}
But, I want any instatiation of ExamplesAndMore
to make sure that it only takes examples of the same type or signature. That is, I want to make sure the examples
list doesn' t contain one member whose attributes
list is of the form <AttributeA, AttributeA, AttributeB>
while another is of the <AttributeA, AttributeA, AttributeA>
or <AttributeA, AttributeA, AttributeB, AttributeB>
.
I could just check everywhere I modify the examples
list.
A comment asked me to be more specific, so I'll give code for how I could do this.
class Example {
List<Attribute> attributes;
public List<Class<? extends Attribute>> getSignature(){
List<Class<? extends Attribute>> result = new ArrayList<Class<? extends Attribute>>();
for(Attribute attribute : attributes){
result.add(attribute.getClass());
}
return result;
}
//..
}
public class ExamplesAndMore {
List<Example> examples;
List<Class<? extends Attribute>> signature;
public ExamplesAndMore(List<Class<? extends Attribute>> signature){
this.signature = signature;
this.examples = new ArrayList<Example>();
}
public void addExample(Example example){
if(!example.getSignature().equals(signature)){
throw new IllegalArgumentException(String.format("This takes examples with signature %s", signature.toString()));
}
examples.add(example);
}
//..
}
But I wonder if this is a good way of going about this, checking for types at runtime doesn't feel nice. Basically I think this code smells a bit. One way this smell might manifest as an actual problem is when I create
class GenericAttribute<T> implements Attribute{\\..}
with the intention of not having an example with an attribute of type GenericAttribute<ClassA>
and one of type GenericAttribute<ClassB>
in the same list. Then type erasure ruins my day.
Also, when I don't need examples to contain arbitrarily many attributes but only a fixed number (say 3), then my problem goes away entirely and I can even check at compile-time by having
class Example<T1, T2, T3> {
//..
}
public class ExamplesAndMore<T1,T2,T3> {
List<Example<T1,T2,T3> examples;
public void addExample(Example<T1,T2,T3> example){\\..}
//..
}
However this approach obviously doesn't extend to examples with arbitrarily many attributes.
Anyway, I feel that I'm missing some obvious technique or something. Is there a nice pattern that deals with this, am I designing this badly? Chances are I'm not the first one doing this.
Also sorry for the lousy title, If I had a better description for my problem I'd probably have better luck searching for an answer and wouldn't have to ask...
-
6Instead of worrying about "what design pattern", focus your question on the specific problem you have (or don't have). Design patterns are just another tool to simplify communicating what a section of code does. Not everything needs to be labeled as a design pattern.user53019– user5301905/26/2015 12:05:57Commented May 26, 2015 at 12:05
-
@GlenH7 "Design patterns are just another tool to simplify communicating what a section of code does." I agree but I'd also add that they are also common consensus good ways of solving a specific problem. I figured a well known one might be applicable here. I can and will implement my "could just check everywhere" solution and edit my question. Maybe that makes it a better question? If not, any other advice as to how to ask a "how do I do this in a nice structural way"-question? Are they just not appreciated here?Hirle– Hirle05/26/2015 13:20:56Commented May 26, 2015 at 13:20
-
3Looking for a pattern instead of solving the problem is a fool's errand. Please edit your question to focus on the specific problem you have with your code, and you are more likely to receive a meaningful answer. You've already laid out most of your real problem in your previous comment. Use that as the base and explain what's not working for you currently.user53019– user5301905/26/2015 13:39:33Commented May 26, 2015 at 13:39
-
@GlenH7 So... I edited in my "could just check everywhere"-solution and edited the question to reflect that. But I somehow wonder if that made it a better question. I'm still just asking "is this a good way of going about things", there is nothing that is not working (as far as I can tell). That's because my question is not of that type. I understand your critique that is based in "Ask specific problems, because those are well suited for Q&A". But I also think you can see what my question is and see that it is just not that specific.Hirle– Hirle05/26/2015 14:06:30Commented May 26, 2015 at 14:06
-
3Why do you need those elements of the lists to be of the same types and lengths?null– null05/26/2015 19:55:20Commented May 26, 2015 at 19:55
2 Answers 2
It seems slightly odd that Example.attributes
can contain a mixture of AttributeA
and AttributeB
objects, but ExamplesAndMore.examples
must contain a 'homogenous' (using that loosely) List
of Example
objects. How are these supposed to be used, for both Example
and ExamplesAndMore
classes? Is there also some kind of implied ordering/equivalence between the entries of ExamplesAndMore.examples
? Could that be another code-smell?
Staying on topic though, you may want a method inside Example
that can reasonably describe the contents of itself... this can be as simple as concatenating the class names of its own entries, as illustrated below. I think that is safe enough since List
have a predictable iteration order.
public String getEntriesDescription() {
return attributes.stream().map(v -> v.getClass().getName())
.collect(Collectors.joining(";"));
}
When you are adding an Example
object to ExamplesAndMore
, you can use that to perform the comparison you want...
public void addExample(Example example) {
// assuming you have a field exampleDescription
if (!exampleDescription.equals(example.getEntriesDescription()) {
throw new IllegalArgumentException("Example description does not match.");
}
examples.add(example);
}
This is quite similar to what you have in mind.
If class AttributeA
implements Attribute
and class AttributeB
implements the same, that means: in some respect AttributeA
behaves like AttributeB
; and the abstraction level on which they do that, is behaving like Attribute
.
This is how polymorphism works!
But, I want any instatiation of ExamplesAndMore to make sure that it only takes examples of the same type or signature.
The correct way of implementing this - according to your initial design would be to have a separate class of Example
which has a List of Attributes of one kind, i.e. you need another Interface
for Example
which allows to distinguish between Example containing AttributeA
and Example containing AttributeB
. Then you are a) sure, that the concrete Example
contains only the attributes you wanted and b) could use the abstraction of the interface to treat them the same in this respect.
In the end, you could define your ExamplesAndMore
class, like intended: containing Examples
, but not classes, but rather interfaces.