Dúvidas com modelagem de classes

Boa noite pessoal. Minha dúvida é bem básica pra quem conhece OO. Eu tava lendo o livro do Deitel sobre Herança e no último exercício ele fala pra aplicar os conceitos de herança em um programa que tenha as subclasses Trapezio, Losango, Quadrado e Retangulo e a superclasse Quadrilatero.

Meu diagrama de classe por enquanto está assim:

http://img216.imageshack.us/my.php?image=capturadatelavg6.png

Minha dúvida é a seguinte: a fórmula do trapézio difere das demais (as outras 3 usam somente duas medidas, enquanto o trapézio usa 3 (B, b e h)). O que eu poderia fazer pra consertar isso?
Eu pensei em fazer mais 2 superclasses que herdam de Quadrilatero: DuasMedidas e TresMedidas, Trapezio herda TresMedidas e o resto de DuasMedidas.
Dentro da TresMedidas eu teria um método sobrescrito para um getB ou algo do gênero. Sobre a área do losango, eu sobrescreveria na class Losango mesmo.

Eu pensei em outra forma também, que é isolar o Retângulo e o Quadrado, onde o Quadrado seria subclasse de Retângulo e passaria super(lado, lado), mas não fiquei muito assim com a idéia.

Também poderia usar mais um construtor de superclasse, recebendo três argumentos (não sei se isso existe), reescrevendo o método de calcular área e um get pro outro lado dentro da propria classe Trapezio…

O que eu poderia fazer?

Olá
Creio que a alternativa mais simples seja adicionar o atributo que falta e reescrever o método calcularArea() da classe Trapezio.
Abraços

Eu também acho isso T.
Mas será que com um nível maior de herança (isso que ele chama no livro) não ficaria melhor? Colocar mais uma superclasse DuasMedidas e outras não ajudaria a fazer mais herança ainda?
To com dúvida se essas duas novas superclasses tem sentido…

É realmente preciso? Nunca é bom complicar a solução, a não ser que essa complicação renda algum benefício. Tornar uma solução “elegante” tem que trazer algo prático. Em quê essa modificação poderia ajudar?

No livro ele fala pra “fazer a hierarquia o mais profunda (com muitos níveis)”, então acho que ele quer isso mesmo…
Se bem que tem mais de uma maneira de ser feito né?

É, sempre existe mais de uma solução para um problema (sendo melhor ou pior, é uma solução).
Bom, tirei meu Deitel da poeira, e tô com o exercício em mãos. Acho que ele quis dizer pra fazer a hierarquia o mais profunda possível com as classes que ele sugeriu, e não inventar outras.

Tava pensando no seguinte:
A classe pai é Quadrilatero (abstrata). Um Retangulo é um Quadrilatero. Um Quadrado é um tipo especial de Retangulo, que possui base e altura com mesma medida (a área é calculada da mesma forma). Um Paralelogramo é também um Retangulo (inclusive, a área é calculada da mesma forma). E um Losango é um tipo especial de Quadrado (sim, os lados de um Losango possuem a mesma medida). Nesse caso, seria preciso adicionar mais dois atributos (suas diagonais) e reescrever o método calcularArea().

Abraços

Ah, faltou o Trapezio. Bom, pra reutilizar ao máximo, Trapezio pode herdar de Retangulo. Aí você precisa adicionar um novo atributo, que é a base menor, para calcular sua área, que é ( B + b ) * h / 2

Hmm… agora to começando a entender.
Você acha que ficaria assim o diagrama de classes?
Eu pediria a base e a altura do paralelogramo, né? E outra… dizem ainda que um paralelogramo é um tipo de trapézio, mas isso cairia mal porque a fórmula do paralelogramo é a mesma do retângulo… então eu teria mais uma vez código duplicado, e é o que não queremos, certo?

http://img230.imageshack.us/my.php?image=capturadatelafd2.png

A única coisa que eu to estranhando é esse trapézio. Ele é um retângulo? Eu faria ele sendo um quadrilátero e adicionando 3 métodos: 1 de getBaseMaior, outro de getBaseMenor e outro de getAltura, mas ainda assim, reescreveria 2 métodos que ainda estão na superclasse (o getX e getY). Eu trataria x e y como base menor e maior respectivamente. O que acha?
E também sobrescrevo o método calcularArea() que ficaria na classe do trapézio mesmo.

Ficaria legal assim será?

Alias, fui codar um pouco agora e vi uma coisa: eu poderia deixar no construtor de Quadrado só passando um parâmetro p/ o construtor e duplicando ele (passando tipo, super(x, x)) pra superclasse. Mas isso estaria errado, porque na classe Losango eu tenho que chamar o super com 2 parâmetros. Então não seria mais correto eu fazer extends Retangulo em vez de Quadrado em Losango?

Abraço!

Nossa, quanta coisa! :smiley:
Vamos por partes:

É, ficaria dessa forma mesmo.

Exato, estamos nos aproveitando de uma vantagem que a herança nos dá: reutilização de código.

[quote=dedejava]A única coisa que eu to estranhando é esse trapézio. Ele é um retângulo? Eu faria ele sendo um quadrilátero e adicionando 3 métodos: 1 de getBaseMaior, outro de getBaseMenor e outro de getAltura, mas ainda assim, reescreveria 2 métodos que ainda estão na superclasse (o getX e getY). Eu trataria x e y como base menor e maior respectivamente. O que acha?
E também sobrescrevo o método calcularArea() que ficaria na classe do trapézio mesmo.[/quote]
Você quem sabe, tente fazer das duas formas e veja qual fica melhor. Afinal de contas, é um exercício :wink:

Conceitualmente, não estaria errado, porque um Losango é um Quadrilatero. Mas em Losango você só vai usar as medidas das diagonais para calcular a área - e essas medidas serão passadas no construtor de Losango. Então, poderia deixar dessa forma mesmo.

Valeu tnaires!
O único empecilho que eu to achando agora é o trapézio mesmo.

Uma superclasse pode ter 2 construtores com diferentes parâmetros?
Ou teria como um sobrecarregar um construtor?

Você pode sobrecarregar construtores à vontade, bastando para isso declarar cada um com parâmetros diferentes.
Não sei bem se essa foi a sua pergunta…

Isso mesmo tnaires.
Obrigado! :slight_smile:
Deu uma solucionada violenta nas dúvidas… sempre me “embanano” nessas modelagens.
Até acho que a classe Trapezio ficou meio mal organizada, mas não tem problema (eu espero).

O diagrama ficou assim:

http://img219.imageshack.us/my.php?image=capturadatelaxm7.png

E a classe Trapezio:

[code]/**

  • Class Trapezio
    */
    public class Trapezio extends Quadrilatero
    {

    private int minorBase = 0;

    /**

    • Constructor of Trapezio
    • @param x X is the larger base
    • @param y Y is the height
    • @param w H is the minor base

    */
    public Trapezio(int x, int y, int w)
    {
    super(x, y);
    minorBase = w;
    }

    /**

    • Acessor method for the minor base
    • @return minorBase The minor base
      */
      public int getMinorBase()
      {
      return minorBase;
      }

    /**

    • Modifier method for the minor base
    • @param newBase The new value of minorBase
      */
      public void setMinorBase(int newBase)
      {
      minorBase = newBase;
      }

    /**

    • Method that will calculate the area of the polygon
    • @return area The area
      */
      public double calculateArea()
      {
      return (((getX() * minorBase) / 2) * getY());
      }
      }
      [/code]

Também me surgiu uma dúvida na hora de fazer a função main. Eu queria colocar tipo, caso o usuário digite que queira calcular a área de um quadrado uma função é chamada pra ler as entradas dele e mostrar. Mas eu preciso do Scanner e tentei declarar ele como uma variável de instância private, mas a main não consegue acessar. É possível fazer isso? Senão meu código fica ruim de ler e tal…
Dá uma olhada:

import java.util.Scanner;

/**
 * Class that will contain the main
 */
public class Controler
{
    /**
     * Method that will show the options for the user
     */
    private void showOptions()
    {
        System.out.println();
        System.out.println("Type the name of the shape that you want");
        System.out.println("Square, Rectangle, Trapezium, Losangle or Parallelogram (or even Quit to exit)");
        System.out.print("Type it  here >> ");
    }
    
    /**
     * Method that will ask for the rectangle values
     */
    private void rectangle()
    {
        
    }
    
    /**
     * Method that answers to the user that that option doesn't exists
     */
    private void dontKnow()
    {
        System.out.println();
        System.out.println("Sorry, but I can't understand that :((");
        System.out.println();
    }
    
    
    public static void main(String[] args)
    {
        Scanner reader = new Scanner(System.in);
        String option;
        Controler cont = new Controler();
        int x = 0;
        int y = 0;
        
        do {
            cont.showOptions();
            option = reader.nextLine();
            option = option.toLowerCase();
            
            if (option.equals("square"))
            {
                System.out.println();                
                System.out.print("side >> ");
                x = Integer.parseInt(reader.nextLine());
                
                Quadrado square = new Quadrado(x);
                System.out.println("The area of the square is " + square.calculateArea());
                System.out.println();
            }
            else if (option.equals("rectangle"))
            {
                System.out.println();                
                System.out.print("width >> ");
                x = Integer.parseInt(reader.nextLine());
                
                System.out.print("height >> ");
                y = Integer.parseInt(reader.nextLine());
                
                Retangulo rectangle = new Retangulo(x, y);
                System.out.println("The area of the rectangle is " + rectangle.calculateArea());
                System.out.println();
            }
            else if (option.equals("trapezium"))
            {
                int z = 0;
                System.out.println();                
                System.out.print("larger base >> ");
                x = Integer.parseInt(reader.nextLine());
                
                System.out.print("other base >> ");
                y = Integer.parseInt(reader.nextLine());
                
                System.out.print("height >> ");
                z = Integer.parseInt(reader.nextLine());
                                
                Trapezio trap = new Trapezio(x, y, z);
                System.out.printf("The area of the trapezium is %.2f\n", trap.calculateArea());
                System.out.println();
            }
            else if (option.equals("losangle"))
            {
                System.out.println();                
                System.out.print("D >> ");
                x = Integer.parseInt(reader.nextLine());
                
                System.out.print("d >> ");
                y = Integer.parseInt(reader.nextLine());
                
                Losango losangle = new Losango(x, y);
                System.out.printf("The area of the losangle is %.2f\n", losangle.calculateArea());
                System.out.println();
            }
            else if (option.equals("parallelogram"))
            {
                System.out.println();
                System.out.print("base >> ");
                x = Integer.parseInt(reader.nextLine());
                
                System.out.print("height >> ");
                y = Integer.parseInt(reader.nextLine());
                
                Paralelogramo parallelogram = new Paralelogramo(x, y);
                System.out.printf("The area of the parallelogram is %.2f\n", parallelogram.calculateArea());
                System.out.println();
            }
            else if (option.equals("quit"))
            {
                System.out.println("Bye");
            }
            else
            {
                cont.dontKnow();
            }                
        } while (!option.equals("quit"));
    }
}

Horrível né? Se eu tivesse uma função como a dontKnow() pra cada formula o código ficaria muito mais legal, né?
Tem como? Eu pensei em fazer um acessador pro Scanner mas acho que não é assim que faz não…

Abraço e valeu pela ajuda!

Você pode escrever a função que trata as entradas individuais, recebendo um objeto Scanner como parâmetro.
Então você cria o objeto Scanner dentro do main e passa para as funções.

Mas e qual seria meu tipo de retorno? Eu teria que criar variáveis de instância fora do main pra setar elas, mas teria o mesmo problema do Scanner, de ter ele como variável de instância e não como variável da main.

Hmmm… é verdade.
Nesse caso, declare as variáveis globais à classe usando a palavra chave static (o método main só acessa atributos estáticos, ou seja, variáveis de classe, e não de instância).