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

5

Hello, I'm 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 persist the created objects in a file, so I created a file that simulates a database, like this:

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 AssinanteFree type for the assinantes list, when the class saves to a file it does not save the other properties of the extended classes and, if I I persist the deepest class in the inheritance, when making a query of the data I have to be converting the same ones.

How to solve?

    
asked by anonymous 28.06.2018 / 13:28

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 must have a id , and id of them must be the same if they are part of the same relationship.

When you save only AssinanteFree , you just insert the record into the FreeRequest table. When you enter a AssinantePremium , you enter the records that belong only to the SubscriberPremium in the SubscriberPremium table and the FreeCriber information in the FreeCriber table, with the same id. The same idea applies to AssinanteVip .

So, when you insert a AssinantePremium registry, it will look like this in your "database":

# AssinantePremium
id  | pontos
123 | 4

# AssinanteFree
id  | nome
123 | Leandro

And when you enter 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 by using Tipo to distinguish them. The same example above would look like this:

# 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 "write" one way (when it goes to your ArrayList ) and convert to another, when you extract the content from it and mount the classes.

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

    
28.06.2018 / 13:34
1

Based on what @dherik said, my solution was a mixture of the 2 templates, like this:

I've added a type field to AssinanteFree and a constructor to each class by defining this type, like this:

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");
    }
// ...
}

I have created my ArrayList in Db as everyone is of type Vip this to be able to simulate the structure of tables:

Now, in any case I will always work with the class Vip Subscribers but depending on the type of that subscriber and where I am using, I will do the implied conversion. The good thing about using this is that I can still add one more method to the Db class where I get 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());
    }
    // ...
}
    
28.06.2018 / 14:24