2

This isn't exactly the definition of implicit type conversion, but I'm curious how many standards I'm breaking with this one...

I'm creating an abstract class in Java that basically casts its variables depending on a string passed into the constructor.

For example:

public abstract class MyClass {

    Object that;

    public MyClass(String input){
        if("test1".equals(input){
            that = new Test1();
        }
        else{
            that = new Test();
        }
    }

    public void doSomething(){
        if(that instanceof Test1){
            //specific test1 method or variable
        } else if(that instanceof Test2)}
            //specific test2 method or variable
        } else {
            //something horrible happened
        }
    }
}

You see what I'm getting at? Now the problem I run into is that my compiler wants me to explicitly cast that into Test1 or Test2 in the doSomething method - which I understand, as the compiler won't assume that it's a certain object type even though the if statements pretty much guarantee the type.

I guess what I'm getting at is, is this a valid solution?

I have other classes that all basically do the same thing but use two different libraries depending on a simple difference and figure this class can help me easily track and make changes to all of those other objects.

1
  • This isn't a horrid design just by itself (it's the whole columns-vs-rows debate) -- however it doesn't fit into the "Java model" which normally encourages polymorphism across types. This type of approach is more often seen in languages with no "real" polymorphism (think C and tagged unions) or in languages that support pattern-matching. Commented Feb 4, 2011 at 21:36

4 Answers 4

10

You are right. This is a horrible way to achieve polymorphism in design. Have you considered using a factory? A strategy object? It sounds like what you are trying to achieve can be implemented in a more loosely-coupled way using a combination of these patterns (and perhaps others).

For the polymorphism of doSomething, for example:

interface Thing {
    public void doThing();
}

class Test1 implements Thing {
    public void doThing() {
        // specific Test1 behavior
    }
}

class Test2 implements Thing {
    public void doThing() {
        // specific Test2 behavior
    }
}

class MyClass {

    Thing _thing;

    public void doSomething() {
        _thing.doThing();    // a proper polymorphism will take care of the dispatch,
                             // effectively eliminating usage of `instanceof`
    }
}

Of course, you need to unify the behaviors of Test1 and Test2 (and other concrete Thing classes, present and planned) under a set of common interface(s).

PS: This design is commonly known as Strategy Pattern.

Sign up to request clarification or add additional context in comments.

7 Comments

+1... and Santa, can this advice be considered an out of season gift?
Ho Ho! This Santa is jolly all year round!
@Santa, great answer... I'm trying to pick it apart and figure out if I can use it to my means. I think I can but I have to try it before I can be sure :D
@Santa, You're name ruins my serious attempts at learning... In your example you declare Thing _thing; is there a significance to the underscore, or is that just standards for declaring an interface object? Also, how would the call to doThing() be interpreted, if both Test objects implemented the method with no parameters how would that call be handled?
@Shaded, The underscore is just a personal preference I've picked up after coding so much in Python (in Python, a semi-private instance variable's name is usually prefixed with an underscore).
|
1

I would create a separate class file. So you would have something like this: 1. You abstract "MyClass" ->within "MyClass" define an abstract method call doSomething...this will force the specific implementation of the method to it's subclasses. 2. Test1 would be the implementation of MyClass which would contain the implementation of the doSomething method 3. Create a utility class that does the check "instanceOf" that check should not be in the constructor it belongs in another class.

So in the end you would have 3 class files an Abstract Class, Implementation of the Abstract and a Class that does the "instanceOf" check. I know this sounds like a lot but it's the proper way to design, for what I think you are attempting to do. You should pick up a design patterns book, I think it would help you a lot with questions like these.

2 Comments

By the way I commend you for at least asking this question...you would be surprised that there are programmers that do horrible things like this.
I don't doubt it, I've seen some terrible code (and probably wrote a bit of it myself in school). Whenever I do something terrible I have this lingering thought of... there MUST be a better way. and that's where SO comes to the rescue!
1

The Open-Closed principle would be better satisfied by moving the object creation outside of this class.

Consider changing the constructor to accept an object that implements an interface.

public MyClass {
  public MyClass( ITest tester ) { m_tester = tester; }
  public void doSomething(){ m_tester.doTest(); }
}

This makes it possible to change the behavior of the class (open to extension) without modifying its code (closed to modification).

Comments

1

The better way to do this is to create an interface which will specify a set of methods that can be guaranteed to be called on the object.

Here's an example:

public interface TestInterface
{
  void doTest();
}

Now you can write your classes to implement this interface. This means that you need to provide a full definition for all methods in the interface, in this case doTest().

public class Test implements TestInterface
{
  public void doTest()
  {
    // do Test-specific stuff
  }
}

public class Test1 implements TestInterface
{
  public void doTest()
  {
    // do Test1-specific stuff
  }
}

Looks really boring and pointless, right? Lots of extra work, I hear you say.

The true value comes in the calling code...

public abstract class MyObject
{
  Test that;

  // [...]

  public void doSomething()
  {
    that.doTest();
  }
}

No if statements, no instanceof, no ugly blocks, nothing. That's all moved to the class definitions, in the common interface method(s) (again, here that is doTest()).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.