0

I often read about that down-casting is a bad thing, and now that I need it, I ask myself what alternatives I have in my example:

I have an Interface A

Three Classes that implement Interface A: X1, X2, X3

These classes describe the same content, but in different "formats" and processes (simply put), which is why I had to divide them.

Now I have a class as follows:

class Test { A obj; }

Depending on the "format", I do either obj = new X1(), obj = new X2() or obj = new X2().

And from now on, I have to use a lot of down-casting within this class to access the classes methods, when working with the object (aside from the methods in A). The only alternative I see, is to make a field for every possible class, of which only one is ever used. Which seems bad as well.

Any suggestions on how a better design would look like?

Thank you for any help.

EDIT: This is my scenario (simply put):

My main class is a container for certain data. One of which is a content attribute (kinda like the body of a HTTP Request). The content can be:

  • plain text (two seperate strings)
  • file (just the file path, md5, etc.) -> Will be loaded into an input stream later on.
  • unknown (bytes) -> I can't and I actually don't have to know. The context later on will give me information about it.

For every type, I created a Class (TextContent, FileContent, UnknownContent). They implement the Interface Content that has the method getBytes(), that will return the content as a byte array.

Usage of the content:

  1. Creation

ContainerClass.setContent(plainText) -> BaseInterface content = new TextContent(plainText) ContainerClass.setFile(file) -> BaseInterface content = new FileContent(file) ContainerClass.setContent(someBytes) -> BaseInterface content = new UnknownContent(someBytes)

  1. My Container object can be saved to a json. The content object will only be saved, if it is Text or File (therefor I can't implement a toJsonObject() in the interface).

  2. When the content is text, I want to check it for certain patterns. I do that outside of the container class with a Matcher Class (e.g. ((TextContent)container.content).getText() -> sadly, the FileContent has no single text it could return, so I can't implement that one either). I could add a getContentAs..() method to my container class that will cast it appropriately, but I feel like I just work around the underlying issue :(

moen
  • 73
  • 5
  • 4
    `Test` should not _care_ whether `obj` is `X1` or `X2` - it should only _know_ or _care_ that `obj` _is an_ `A`! Even that fact that `Test` knows that there is a `X1` or `X2` is a **design mistake**. You need to investigate _why_ `Test` needs to know this to work out what is wrong with the design; but downcasting is a sign that the abstraction as at the wrong layer. E.g. if `Test` is constantly deciding on behaviour depending on the runtime type of `obj` then maybe a Visitor Pattern would be better. – Boris the Spider Oct 04 '16 at 15:44
  • 2
    If the processes are similar but have different implementations, why not declare an abstract method in the interface and have each class overrride the methods? – Jason White Oct 04 '16 at 15:47
  • Could you post some example code? Maybe a (very) simplified situation in which you are downcasting? – Boris the Spider Oct 04 '16 at 15:58
  • @BoristheSpider Thank you for your answer. I know I did a design mistake somewhere, but it is kinda hard for me to figure out how to resolve this. I updated my post to show my scenario. Maybe you have some thoughts on that. Thanks! – moen Oct 04 '16 at 18:07
  • @jmw5598 Each type has some "unique" methods that they don't share with others. To access them, I need to down-cast. – moen Oct 05 '16 at 08:39
  • Okay, I begin to see. But 1) why can't your `BinaryContent` simply turn itself into Base64 for saving into JSON (`content: aAbCx==` etc). 2) you can use `Scanner` to search for regex in a `Stream` with [`findWithinHorizon`](https://docs.oracle.com/javase/8/docs/api/java/util/Scanner.html#findWithinHorizon-java.util.regex.Pattern-int-) negating any need to differentiate types of content. If all these are `Content` then they should behave as such, if not then you need to redesign your hierarchy. – Boris the Spider Oct 05 '16 at 10:18
  • Good point on both. Turning the BinaryContent into a Base64 via getText() was something I was thinking about, too. But I did not know about the posibility to search a stream for regex. That could solve a few problems. But what do you mean by "need to differentiate types of content". Is it better to put all that in just one class, or that I don't have to differentiate the type when I use them? – moen Oct 06 '16 at 07:27

1 Answers1

-3

Since the data is the same, it sounds like you only want one class here, not multiple. Keep the formatting separate from the data.

puhlen
  • 8,400
  • 1
  • 16
  • 31
  • Multiple classes are *fine*. – Makoto Oct 04 '16 at 15:50
  • OO programming is about _behaviour_ as well as _data_. The whole point being that behaviour and data are stored together in a "class", with an `interface` exposing common **behaviour**. This answer makes little to no sense. – Boris the Spider Oct 04 '16 at 15:51