2

I have an abstract class that we will call Vehicle. Classes Diesel, Electric, and Gas extend Vehicle. Those 3 classes have their own methods and variables, as well as what they inherited from Vehicle.

I want to put variables from the Diesel, Electric, and Gas objects into the same table within a database. A record created from a Electric object would simply have a few empty columns where it didn't have the same variables as the Diesel object did, etc.

I want to make methods to insert rows, update rows, and retrieve rows, all accepting objects from these classes as arguments.

I am searching for the best solution to this problem, as I do not think that I know what it is. So far I can think of a few solutions, but I do not like any of them:

  1. Create 4 of each of the following methods: insert, update, delete, and retrieve/select. This would result in a lot of duplicate code.

    public void AddVehicle(Diesel diesel)
    public void AddVehicle(Electric electric)
    public void AddVehicle(Gas gas)
    
    public Electric ViewVehicles()
    public Diesel ViewVehicle()
    public Gas ViewVehicle()
    
    public void RemoveVehicle(Diesel diesel)
    public void RemoveVehicle(Electric electric)
    public void RemoveVehicle(Gas gas)
    
    public void UpdateVehicle(Diesel diesel)
    public void UpdateVehicle(Electric electric)
    public void UpdateVehicle(Gas gas)
    
  2. Create each method so that it can accept any object: (Rather than taking any object, is there a way to specify a list of objects which it can take, but still only take one of them? ie. public void AddVehicle (Diesel diesel || Electric electric || Gas gas))

    public void AddVehicle(Object vehicle)
    
    public Object ViewVehicle(Object vehicle)
    
    public void RemoveVehicle(Object vehicle)
    
    public void UpdateVehicle(Object vehicle)
    
  3. Create a "master" class that can contain any of the classes (Diesel, Electric, or Gas) which is then passed into the insert/update/view/delete methods. (Setting the insert/update/view methods up to accept this "master" class as an argument.) Incomplete constructors for the "master" class:

    public MasterVehicle(Diesel diesel)
    public MasterVehicle(Electric electric)
    public MasterVehicle(Gas gas)
    
  4. Some other solution which is proper, cleaner, and makes more sense.

But what might #4 be?

Lemmings19
  • 1,383
  • 3
  • 21
  • 34
  • I made a minor, yet huge mistake in my description which rules out one easy solution. The classes **extend** Vehicle instead of *implementing* Vehicle. I had previously wrote that they implemented vehicle. Apologies. – Lemmings19 Dec 24 '12 at 02:13
  • Using `public static PreparedStatement prepareVehicleStatement(Vehicle vehicle){/*code*/}` for most of the duplicate code in many of the classes avoids much of the code duplication, but is not a full-on solution to the problem. – Lemmings19 Dec 24 '12 at 07:55
  • @Lemming19 implement in english I think is appropriate to describe your scenario. In java the keyword `implements` is used for a different purpose, but I found your sentence correct from the semantic point of view. So no worries; your competence and communication skills are out of questions. – Luigi R. Viggiano Dec 25 '12 at 02:17

5 Answers5

1

Regarding your option two: First of all you can have Vehicle instead of Object as long as Diesel, Electric, and Gas implement/extend Vehicle. It should do the job.

Additionally for ViewVehicle in option two :

public Object ViewVehicle(Object vehicle)

you could use a generic method so that it would always return the same type that it gets in. more info here: http://docs.oracle.com/javase/tutorial/extra/generics/methods.html ...although I am not clear on why you try to send an object to a method that then is returned from the method.

rgasiore
  • 140
  • 7
  • I accidentally wrote that the classes implemented Vehicle instead of extending it. With them extending Vehicle, I do not believe that I can simply pass in Vehicle. The extending classes have their own methods and variables which Vehicle does not have. This is a problem when I attempt to reference a variable that is inside of Diesel, but not inside of Vehicle. – Lemmings19 Dec 24 '12 at 02:19
  • Just tried in order to confirm, and attempting `public static Long addVehicle(T vehicle)` produces the same problems that addVehicle(Vehicle vehicle) has; calling variables or methods that do not exist in Vehicle doesn't work. Using generics does work, however, when attempting to remove some code duplication by providing an extra `Vehicle` method for each of the CRUD operations. Then, using that `Vehicle` method to cover some of the previously duplicated code in each method. – Lemmings19 Dec 24 '12 at 05:16
1

As you said at beginning of your question:

I have an abstract class that we will call Vehicle. Classes Diesel, Electric, and Gas implement Vehicle.

So Vehicle is the superclass, and Diesel, Electric and Gas are the specializations.

The tables on the db can reflect this layout

Vehicle (id, brand, price, type) // type could help you selecting electric,gas,diesel

Electric(vehicle_id, volts, amp, ohms, other_specific_stuff)
Gas(vehicle_id, pressure, gas_type, other_specific_stuff) 
Diesel(vehicle_id, other_specific_stuff) 

when you query your vehicle you can use left outer join

select * from vehicle as v
    left outer join electric as e on (v.id = e.vehicle_id)
    left outer join gas as g on (v.id = g.vehicle_id)
    left outer join diesel as d on (v.id = d.vehicle_id)

Then you use the type field to map to a specific subclass.

When you store, or update the objects in the db, you need a method to store the common parts:

void update(Vehicle v) { 
    // store v.* in Vehicle table
}

and a method that store the rest for the specific car

void update(Electric e) {
    update(v)
    // store e.* on Electric table
}

Same thing for deletion and insertion.

Luigi R. Viggiano
  • 8,659
  • 7
  • 53
  • 66
  • Thanks for the notes on the SQL for the tables, however this doesn't do anything to avoid having to create 4 methods for each operation with just the example classes. (Create, Read, Update, Delete for `Vehicle`, `Diesel`, `Electric`, `Gas`) 16 methods in total. Creating the `Vehicle` method for each operation eliminates a lot of duplication, but I'm still wondering if there is a more efficient way. – Lemmings19 Dec 24 '12 at 04:24
  • Also, I made a mistake in the question (which doesn't affect your answer) in saying that the classes implemented `Vehicle`. They actually *extend* vehicle. My mistake, sorry. – Lemmings19 Dec 24 '12 at 04:28
  • Hibernate does most of the additional mapping for the specific classes, but in a clean design you need a common table for the common data, and a specific table for each specialization; then you may need a field in the common table to indicate to hibernate the type as 'discriminator'; the approach is similar to the one I showed in my answer. See: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/inheritance.html This link analyses several approaches to your problem; you may give it a read. – Luigi R. Viggiano Dec 24 '12 at 09:19
  • Actually, if you see, there is no duplication in the update methods I've posted. There is Electric.update() which *uses* Vehicle.update() avoiding duplications. I'm not a super-fan of hibernate, but it does all the job for you in your example, if you want to avoid writing the OR-mapping code. – Luigi R. Viggiano Dec 24 '12 at 09:27
1

Fortunately, other people had to deal with this problem as well :)

You have actually two problems here. One is the design of the API as you have identified, and the other is how to handle the actual storing/reading to the DB.

As for the first problem, I'd go with your option #2 because it best follows the OO design principle. But I won't make the method accept an Object. Instead, make it accept an Vehicle type.

For the second problem, you can have a look at the following link: Entity Inheritance Mapping Strategies. Even if you are not going to use JPA (or any ORM framework), these are probably the strategies you wanna look at.

Here is a more detailed explanation on the single table strategy mentioned in the above link. This should give you a good idea --> Inheritance: Single Table Strategy

Enno Shioji
  • 26,542
  • 13
  • 70
  • 109
  • I made a minor, yet huge mistake in my description which (I believe) rules out making the methods accept Vehicle instead of Diesel, Electric, or Gas. The classes **extend** Vehicle instead of *implementing* Vehicle. I had previously wrote that it implemented vehicle. Apologies. – Lemmings19 Dec 24 '12 at 02:14
  • @Lemmings19: You can still use `Vehicle` type. When a child class extends a parent class, the child class does implement the parent classes methods :) – Enno Shioji Dec 24 '12 at 02:26
  • True, but when I try to call something that exists in, say, `Diesel`, it can't find it because it's only looking in `Vehicle`. Works fine if I only need to call things that exist in `Vehicle`, though. In any event, thanks for the response! – Lemmings19 Dec 24 '12 at 04:12
1

Generic DAO is what you might be interested in. Trying to avoid writing the long answer so here is the link. j-genericdao

You can find lot of variation of generic dao on internet.

Community
  • 1
  • 1
Parvez
  • 631
  • 1
  • 6
  • 19
1

I would recommend to decouple persistence layer (the relation database) from the object oriented model layer (Java) and forget about "CRUD" operations and think more about object behavior.

Would be nice to have an class Garage implementing a collection of Vehicles:

interface Garage extends Collection<? extends Vehicle> {
  Vehicle find(int id);
}

Then, you can use it like:

Garage garage = new GarageInSql(/* your persistence layer details */);
Electric vehicle = Electric.class.cast(garage.find(123));
int mileage = vehicle.mileage();
vehicle.rename("Chevrolet");
garage.remove(vehicle);

If you need to control transactions introduce one more method Garage#commit().

yegor256
  • 102,010
  • 123
  • 446
  • 597