radrow
radrow

Reputation: 7139

Java - cloning through extending

Imagine situation:

I have got two classes

public class Fruit{
}

and:

 public class FruitOnSale extends Fruit{
     int price;
     public int getPrice() or something
 }

My program has one fruit and one fruitOnSale object. I would like to create completly new (clone) fruit2 [Fruit] based on the fruitOnSale (for example somebody has bought fruitOnSale and it needs to appear in his bag but as a normal fruit cause it is no more in shop). The method getPrice and field price are deleted during it. Then backwards - somebody creates a shop (...) and puts his fruit on sale. Now I need to convert fruit to FruitOnSale (I want it to be in the shop as 'on sale' and in my bag as 'normal', but as independent objects).

These classes will be rebuilded, modified and lots work may be done on them, so I don't want to manualy rewrite them just by copying every field because it's sensless. Is it possible through clonning? Or have you got better ideas?

Upvotes: 0

Views: 70

Answers (2)

Vivin Paliath
Vivin Paliath

Reputation: 95538

Use composition:

public class Fruit {
   // your regular fruit class
}

Your "fruit on sale":

public class FruitOnSale {

    private Fruit fruit;     
    private int price;

    public FruitOnSale(Fruit fruit, int price) {
        this.fruit = new Fruit(fruit.getName());
        this.price = price;
    }

    public Fruit getFruit() {
        return Fruit(fruit.getName());
    }
}

Now you can construct FruitOnSale instances by passing in a Fruit instance, and you can get a Fruit instance from FruitOnSale instance as well. Notice that when you initialize a FruitOnSale instance with a Fruit instance, it creates a "copy" of the Fruit instance. It's good to do this, otherwise you could modify the original Fruit instance, which would then affect the FruitOnSale instance. This is not what you want, if you are assuming that these are immutable. Similarly, getFruit() doesn't return a reference to the internal instance, but creates an entirely-new Fruit instance as well.

While solving this particular example using inheritance does not violate the Liskov Substitution Principle, in general it is a good idea to think about the impact of extending the base class. For example, there might be some things that you can do with Fruit, that don't make sense with FruitOnSale (i.e., could lead to inconsistent, illogical, or unpredictable behavior); this would be an example of LSP being violated.

Upvotes: 3

initramfs
initramfs

Reputation: 8405

Cloning and Inheritance are two very different and very unrelated concepts in which both is unlikely to help you in your current case, if I understand you correctly.

Cloning normally refers to when you want to duplicate existing objects at runtime, thus the term should be avoided when used in a programming context unless the subject matter actually has relevance to the concept of object cloning.

Inheritance is used in contexts where you want to provide a more specific version or an alternative implementation of something, thereby extending the features of the more vague/abstract version with addition/altered functionality.

To give an example, we can declare two classes as follows:

public class WoodenFurniture{
    public void burn(){
        // Implementation here
    }
}

and

public class WoodenChair extends WoodenFurniture{
    public void sit(){
        // Implementation here
    }
}

Note in this case, WoodenChair is a more specific version of WoodenFurniture and thus WoodenChair inherits all functionality of WoodenFurniture, including the aforementioned burn() method.

Whilst your Fruit and FruitOnSale class demonstrates such a relationship, where FruitOnSale is a more specific version of Fruit, your application of inheritance is incorrect.

In your question body, you suggest mutating a Fruit object between multiple states carrying different information should be done through inheritance, which is only true if you are continuously adding or specifying detail to your current object. In this particular case, composition should be used, as described by Vivin Paliath.

But in the case you are describing, you want to repeatedly change or toggle between different states of the same object. Especially by saying method getPrice and field price are deleted during it you demonstrate inheritance is not what you want. This should be done by adding fields to your original object to append state-specific information as opposed to the creation of multiple classes, as follows:

public class Fruit{
    private boolean onSale = false;
    private int price = 0;

   /**
    * Retrieves the price of the fruit if it is on sale.
    */
    public int getPrice(){
        // Check if the fruit is in the onSale state
        if(!onSale){
            // Rather than throwing exception, you can alternatively return an invalid price e.g. -1 to denote "not for sale"
            throw new IllegalStateException("not on sale");
        }

        return price;
    }

    public void putOnSale(int price){
        this.price = price;
        onSale = true;
    }

    public void takeOffSale(){
        onSale = false;
    }
}

The class above represents an example implementation of something on the lines of what you want. In this case, we do not need to go through the trouble of converting between object types nor break any inheritance policy regarding how objects should interact.

Upvotes: 0

Related Questions