Eu acho que a pergunta é interessante. Não é o problema em si. É devido ao uso indevido de herança. Um uso incorreto muito comum.
Por que eu digo isso?
Because I try to reduce redundant code I created an abstract class defining a general configuration, let's call it "GeneralConfiguration".
Esse é um dos piores motivos para usar a herança. O objetivo por baixo das classes abstratas é muito mais sofisticado do que nos salvar da duplicação de código .
For each "shared" property GeneralConfiguration defines an interface
Aqui, os detalhes de implementação foram transferidos para algo que deve ser abstract . Isso mata o que as classes abstratas fazem bem, abstrai-nos dos detalhes.
A única coisa em comum entre A e B é que, conceitualmente , ambas são configurações , mas aqui terminam suas semelhanças.
Como é isso?
Now, if the application user enters one configuration(A or B) it should be possible to access in a general and easy way all given methods/properties of the configurations.
Por design, ambos se transformaram em algo diferente. Ambos foram projetados como dois componentes diferentes e, agora, estamos tentando corrigi-lo com herança. A herança não funciona dessa maneira.
No papel, ambos podem ter o mesmo significado e propósito, mas esse recurso não foi transferido para o design.
Se quisermos nos abstrair dos "detalhes" (A ou B), a configuração deve ser totalmente agnóstica para eles .
Exemplo:
public Configuration {
Map<enum,Object> properties;
public Configuration(){
properties = new HashMap<enum,Object>();
}
public Object getProperty(enum propertyName){
return properties.get(propertyName);
}
public void setProperty(enum propertyName, Object value){
properties.put(propertyName, value);
}
}
Agora, não há A nem B (e a herança se torna desnecessária) .
Como implentamos A e B?
Agora, podemos implementar "detalhes" sobre a abstração. Por exemplo, implementando o padrão de delegação
public ConfigA {
Configuration config;
public ConfigA(Configuration config){
this.config = config;
}
public String getA(){
return (String) config.getProperty(MyEnumA.A);
}
public void setA(String value){
config.setProperty(MyEnumA.A,value);
}
...
}
Se precisarmos de "detalhes", usamos ConfigA / ConfigB . Nós usamos Configuração para o resto.
Nota: Quando digo abstração , não estou me referindo apenas a classes abstratas. Estou me referindo ao conceito.