How to make an implicit conversion (inheritance) without losing information

Asked

Viewed 103 times

5

Hello, I am doing a project for college where I have to implement 3 classes. These classes inherit from each other as in the model below:

public class AssinanteFree {
    protected int id;
    public int getId() { return id; }
    public void setId(int v) { id = v; }

    protected String nome;
    public String getNome() { return nome; }
    public void setNome(String v) { nome = v; }
}

public class AssinantePremium extends AssinanteFree {
    protected double pontos;
    public double getPontos(){ return pontos; }
    public void setPontos(double v) { pontos = v; }
}

public class AssinanteVip extends AssinantePremium {
    protected String criadoEm;
    public String getCriadoEm() { return criadoEm; }
    public void setCriadoEm(String v) { criadoEm = v; }
}

I have to make the persistence of objects created in a file, so I created a file that simulates a database, so:

public class Db {
    // construtores
    private Db() {
        assinantes = new ArrayList<>();
    }
    private Db(int i) throws IOException {
        try {
            get();
        } catch (IOException e) {
            assinantes = new ArrayList<>();
            set();
        }
    }
    // implementação singleton
    private static Db instance;
    public static Db getInstance() throws IOException {
        return instance == null ? instance = new Db(0) : instance;
    }
    // getters & setters
    private ArrayList<AssinanteFree> assinantes;
    public ArrayList<AssinanteFree> getAssinantes() {
        return instance.assinantes;
    }
    public void setAssinantes(ArrayList<AssinanteFree> v) {
        assinantes = v;
    }
    // persistencia
    private void get() throws IOException {
        try (FileReader reader = new FileReader("db.json")) {
            Gson gson = new Gson();
            instance = gson.fromJson(reader, Db.class);
        }
    }
    private void set() throws IOException {
        try (Writer writer = new FileWriter("db.json")) {
            Gson gson = new GsonBuilder().create();
            gson.toJson(this, writer);
        }
    }
}

My problem is this, if in my "database" I have a type AssinanteFree to the list assinantes, when the class saves in file it does not save the other properties of the extended classes and, If I persist the deepest class in the inheritance, when making a query of the data I have to keep converting the same.

How to solve?

2 answers

3

You can simulate that in your database there are 3 tables (3 different ArrayList), one for each type of subscriber and each table needs to have a id, and the id theirs must be the same if they are part of the same relationship.

By just saving AssinanteFree, you just enter the record into the Subscriber free table. By inserting a AssinantePremium, you enter the records that belong only to Subscribertepremium in the Subscriber table and the Subscriber information free in the Subscriber table free, with the same id. The same idea applies to AssinanteVip.

Therefore, when entering a record of AssinantePremium, will look like this in your "database":

# AssinantePremium
id  | pontos
123 | 4

# AssinanteFree
id  | nome
123 | Leandro

And when inserting another Premium subscriber:

# AssinantePremium
id  | pontos
123 | 4
456 | 7

# AssinanteFree
id  | nome
123 | Leandro
456 | Leonardo

# AssinanteVip
id  | criadoEm
456 | 2018-06-28

Another idea is to use the same "table" (with a single ArrayList). You would have all fields of all classes in a single table, but you would know which class it belongs to using a Tipo to distinguish them. The same example above would look this way:

# Assinante
id  | nome     | pontos | criadoEm    | Tipo
123 | Leandro  | 4      |             | AssinantePremium
456 | Leonardo | 7      | 2018-06-28  | AssinanteVip

Note that in both cases you need to "record" in a way (when it goes to your ArrayList) and convert to another, when will extract the content from it and assemble the classes.

The tips above are inspired by how JPA/Hibernate treats the same situations.

  • Let me get this straight... so in theory I have 3 Arraylist’s where in the first I have the AssinanteFree, in the second I have the complement of AssinantePremium and in the third I have the complement of AssinanteVip?

  • 1

    In my first idea, yes, each simulating a table. I’m writing another idea now with just a single table.

  • Because at first I was doing with only 1 table, the problem is that when doing the inverse conversion was not very "cool"

  • I believe that if I really do as you say it looks better... because so if I really want to know the person’s type, or I can search in the other Arraylists the id or I can add a type field in the free... so it solves the problem...

  • 1

    I edited and added another option, using the same Arraylist.

1

Based on @dherik falo, my solution was a mix of the 2 models, thus:

1º I added a type field to the AssinanteFree and a constructor to each class defining this type, thus:

public class AssinanteFree {
    public AssinanteFree(String tipo){ 
        this.tipo = tipo == null || !tipo.equals("") ? "Free" : tipo; 
    }
    protected String tipo;
    public String getTipo(){ return tipo; }
    // ...
}

public class AssinantePremium extends AssinanteFree {

    public AssinantePremium(String tipo) { 
        super(tipo == null || !tipo.equals("") ? "Premium" : tipo);
    }
    // ...
}

public class AssinanteVip extends AssinantePremium {

    public AssinanteVip(){
        super("Vip");
    }
// ...
}

2nd created defined my ArrayList in Db like everyone being of the type Vip this to be able to simulate the structure of tables:

Now, in any case I will always work with the Vip Subscribers class but, depending on the type of that subscriber and where I am using, I will do the implicit conversion. The good thing about using so is that I can still add one more method to class Db where I get the Get based on a filter, like this:

public class Db {
    // ...
    public ArrayList<AssinanteVip> getAssinantes(String tipo) {
        return (ArrayList)instance.assinantes.stream()
                   .filter((a)-> a.tipo.equals(tipo))
                   .collect(Collectors.toList());
    }
    // ...
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.