Superclass can become subclass?

Asked

Viewed 688 times

8

I have the classes Versao, that a general version, VersaoFirmware and VersaoSoftware. In practice the user can add one or more versions to an equipment. However, at first, it is not yet known what type of version. Then I’ll add the versions with new Versao(). When he finishes adding the versions, he must select which version type is, VersaoFirmware or VersaoSoftware.

What I tried to:

And I saw the schematic and I thought, "This is clearly an inheritance case. I have a superclass Versao which has properties common to all version types. And I have the subclasses VersaoFirmware and VersaoSoftware who inherit from the superclass, for they have everything Versao has, more or other different method.".

class Versao {
    private int id;
    private String name;

    public Versao() {}
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}


class VersaoFirmware extends Versao {
    private String outraCoisa;

    public VersaoFirmware() {}
    public String fazOutraCoisa() {
        return outraCoisa;
    }
}


class VersaoSoftware extends Versao {
    private String outraCoisa;

    public VersaoSoftware () {}
    public String fazOutraCoisa() {
        return outraCoisa;
    }
}

However, I came across the following problem: after the user selects the version type, how do I turn the general version to the specific version? Should I create new instances of each type and pass the attributes manually? For example:

Versao versao = new Versao();
versao.setName("Versao");

VersaoSoftware versaoSoft = new VersaoSoftware();
versaoSoft.setName(versao.getName());
versaoSoft.setId(versao.getId()); // ....

But what if Versao have 50 properties, I will have to pass all by hand? And so also seems to me meaningless use of inheritance.

Complementing the title of the question, I tried to reverse the hierarchy of classes, solved the problem, but it is totally meaningless to leave exposed methods specific to the general version.

I read about Copy of Builder, that briefly creates a subclass from a superclass. But in the same way, you would have to pass all the properties manually.

How do I model this system? Is there any other strategy to solve this problem?

  • I asked a similar question in the OS: http://stackoverflow.com/questions/27134754/subclass-instance-to-superclass. Although I accept the answer, I asked this question here in more detail to see if this strategy is correct.

  • 1

    Use a reflective method to copy properties. One of the best known classes is BeanUtils.copyProperties

  • Ask the user to inform before the type of the version; then you will know the type of object to instantiate before the user starts entering the details of each version. If each version type has specific information, nor does it make sense for the user to inform the version type after informing the versions themselves.

  • @Caffé I agree with you. But you know how it is: "The boss asked". Then our biggest job is to try to convince the boss that this is not the best way. In case you don’t make it, let the programmer turn around.

  • @Earendul You can also solve with application mechanics (maintain a data structure list and create business objects when you already have the minimum necessary information available), without corrupting modeling by a very specific usability need.

  • 1

    Related: "It is right to give greater preference to composition than inheritance?" I would say that this is a case where it is justified to give up the inheritance, although the language syntax does not help (if you want a single object with all the relevant properties, something you will have to do by hand). By the way, do you really need to create the class object before the user even finishes filling in the fields on the screen? Can’t do this at the end? And what happens if the user chooses the wrong type, he will have to cancel everything and start again?

  • @mgibsonbr No need, so much so that I have already changed the mode, first the user enters the data and selects the type, only then he can add the versions of that type. However, I’m still in doubt in a case where I couldn’t select the type before.

Show 2 more comments

4 answers

11


Well, the concept of inheritance is somewhat misunderstood, though simple. Inheritance in O.O. isn’t just reusing code (different from what many say), it’s just a consequence. Inheritance shall be applied when a class is clearly an extension of another class.

In your situation, you might not use inheritance, because from what I can tell, if the idea is just to reuse attributes (and not behaviors), you could opt for composition:

public class DadosVersao {
   private Tipo atributoA;
   private Tipo atributoB;
}

public class VersaoSoftware {
   private DadosVersao atributos = new DadosVersao();
}

public class VersaoHardware {
   private DadosVersao atributos = new DadosVersao();
}

You could receive the attributes in the class constructor.

But if you really want to use inheritance, I think it makes sense for your Versao class to be abstract, because it makes no sense to exist alone.

public abstract class Versao {
...
}

If you want to create a stereotype for the version, just create an abstract method that forces the child classes to implement:

public abstract class Versao {
   public abstract String getTipo();
}

public class VersaoSoftware extends Versao {

   @Override
   public String getTipo() {
       return "software";
   }

}

And yet, if it is popular data of the daughter class with attributes of the mother, simple:

public class VersaoSoftware extends Versao {

   public VersaoSoftware(Atributo atributoEspecificoSoftware, Atributo atributoVersao) {
      this.atributoEspecificoSoftware = atributoEspecificoSoftware;
      super.setAtributoVersao(atributoVersao); //pode usar um atributo protected diretamente ou, ainda, não precisa do super se for o setter publico ;P
   }

   @Override
   public String getTipo() {
       return "software";
   }

}

There are many options for this, but the ideal is to use inheritance when it makes sense to specialization of the class in the behavioural aspect and not only in the characteristic.

2

I think you’ve already found the solution. The best way, and a well-known practice of architecture, is the build copy. Something like:

 Versao versao = new Versao();
 versao.setName("Versao");

 VersaoSoftware versaoSoft = new VersaoSoftware(versao);
 VersaoFirmware versaoFirm = new VersaoFirmware(versao);

In the base class, you would make a function that would be called by the constructor functions:

 public Clone(Versao origem)
 {
       this.prop1 = origem.prop1;
       this.prop2 = origem.prop2; // etc...
 }

This makes it possible for you to change the base class without affecting the other constructors, after all they would always call the same function no matter which version of the software.

You could follow this same architecture for eventual future changes in your daughter classes (or daughters extending daughters). So you don’t have to worry about setting the mother class properties in the daughter classes, which would be a dangerous repeat of code if someone ever extended the mother class and forgot to look at all the daughters.

  • In my case I think that’s right. But I kept thinking, "what if I had a lot of properties? Would I have to go through them all manually? Is there not a better solution for this?" But apparently this is the best way to do it?.

  • @Earendul, I think you’re right about most properties, but not all of them are trivial. Sometimes you need to do some operation to set each of them, etc. Only one thing I forgot to mention and then I add my answer: to Clone should always be called as base.Clone(origem); in all child class builders (perhaps in one method Clone ). It is important that there is no override initial mother class by an intermediate mother class, shall have a sequence of calls.

2

Problem

I think you are thinking wrong (you should write to us your request and not what you have already thought). I will try to focus on the description of your problem. Going by parts, you said that:

  • Has a Equipment
  • In practice the user can add one or more versions for a equipment
  • When he has finished adding the versions, he must select what is the type of the versions, Versaofirmware or Versaosoftware (this one here was confused, after all a equipment has N versions or equipment has 1 version?. I used the worst case scenario, being 1 equipment for N version, if necessary, you adapt later)

See your diagram:

diagrama

Being:

  • 1 Version has 1 Tipoversao (I used Num to simplify, but you can exchange for a higher level entity)
  • 1 Version has N Properties

Out that I imagine you want

saida esperada

Classes for testing

public class Equipamento {
    public String nome;
    public List<Versao> versoes = new ArrayList<Versao>();
}

public class Versao {
    public TipoVersao tipoVersao;
    public List<PropriedadeVersao> propriedadesVersao = new ArrayList<PropriedadeVersao>();
}

public enum TipoVersao {
    HARDWARE, SOFTWARE
}

public class PropriedadeVersao {
    public String nome;
    public String valor;
}

Sample section with classes

    Equipamento equipamento = new Equipamento();
    equipamento.nome = "TV Samsung";

    // VERSAO DE HARDWARE

    Versao versaoHardware = new Versao();
    versaoHardware.tipoVersao = TipoVersao.HARDWARE;

    PropriedadeVersao propriedadeVersaoChipset = new PropriedadeVersao();
    propriedadeVersaoChipset.nome = "Chipset";
    propriedadeVersaoChipset.valor = "111.1";
    versaoHardware.propriedadesVersao.add(propriedadeVersaoChipset);

    PropriedadeVersao propriedadeVersaoAquecimentoMaximo = new PropriedadeVersao();
    propriedadeVersaoAquecimentoMaximo.nome = "Aquecimento Maximo";
    propriedadeVersaoAquecimentoMaximo.valor = "20º";
    versaoHardware.propriedadesVersao.add(propriedadeVersaoAquecimentoMaximo);

    equipamento.versoes.add(versaoHardware);

    // VERSAO DE SOFTWARE       

    Versao versaoSoftware = new Versao();
    versaoSoftware.tipoVersao = TipoVersao.SOFTWARE;

    PropriedadeVersao propriedadeVersaoFabricante = new PropriedadeVersao();
    propriedadeVersaoFabricante.nome = "Fabricante";
    propriedadeVersaoFabricante.valor = "Microsoft";
    versaoSoftware.propriedadesVersao.add(propriedadeVersaoFabricante);

    equipamento.versoes.add(versaoSoftware);

    // IMPRIME DADOS

    System.out.println("Equipamento: " + equipamento.nome);

    for (Versao versao : equipamento.versoes) {
        System.out.println("- Versao: " + versao.tipoVersao.toString());            
        for (PropriedadeVersao propriedadeVersao : versao.propriedadesVersao) {
            System.out.println("-- " + propriedadeVersao.nome + " = " + propriedadeVersao.valor);
        }
    }

2

Use composition:

// coisas comuns, ainda não definiu se firmware/software
Versao versao = new Versao(1, "0.0.1");
// se firmware...
VersaoFirmware firmware = new VersaoFirmware(versao, "Firmware");
// se software...
VersaoSoftware software = new VersaoSoftware(versao, "software");

Having as super class:

abstract class PossuiVersao {
  final Versao versao;
  PossuiVersao(Versao versao) {
    this.versao = versao;
  }
  // métodos delegam para Versao, 
  // assim PossuiVersao comporta-se como Versao.
  public int getId() {
    return versao.getId();
  }
  public String getName() {
    return versao.getName();
  }
}

And the subclasses:

class VersaoFirmware extends PossuiVersao {
  final String coisaFirmware;
  VersaoFirmware(Versao versao, String coisaFirmware) {
    super(versao);
    this.coisaFirmware = coisaFirmware;
  }
  void somenteFirmware() {
    // comportamento específico firmware
  }
}

class VersaoSoftware extends PossuiVersao {
  final String coisaSoftware;
  VersaoSoftware(Versao versao, String coisaSoftware) {
    super(versao);
    this.coisaSoftware = coisaSoftware;
  }
  void somenteSoftware() {
    // comportamento específico software
  }
}

Browser other questions tagged

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