Relazione sull’esercitazione di laboratorio



Scaricare 1.05 Mb.
10.12.2017
Dimensione del file1.05 Mb.

Relazione sull’esercitazione di laboratorio
Di: Stefano Morini

Data esercitazione: 17/05/2001


Descrizione del problema



  • Costruire un ADT DrawPad che fornisca servizi per disegnare triangoli, rettangoli e cerchi. Si dispone già dell’astrazione SimpleDrawPad di cui è fornita documentazione e *.class. Si progetti e sviluppi un componente software che crei un oggetto DrawPad e lo testi richiedendo il disegno di alcune linee, triangoli, rettangoli e cerchi.

  • Si vuole poter aggiungere figure senza dover tutte le volte creare una nuova astrazione derivante da DrawPad. Data la classe astratta Shape, derivare nuove classi concrete relative alle figure che si vogliono avere ( triangolo, rettangolo, cerchio…), ognuna che specifichi come disegnarsi dato un DrawPad o un SimpleDrawPad. Si progetti e sviluppi un componente software che testi le nuove classi; per fare questo di dispone dell’astrazione SimpleDrawPad, fornita in principio.

Analisi


DrawPad è un classe che eredita da SimpleDrawPad la capacità di disegnare linee e punti, fornendo in aggiunta servizi per disegnare semplici figure geometriche (triangoli, quadrati, rettangoli, cerchi ,pentagoni, ecc…).

In aggiunta DrawPad3D eredita ed estende DrawPad fornendo servizi base di disegno 3D.

DrawPad3D è anche un DrawPad che è anche un SimpleDrawPad.

Ovunque si usa un SimpleDrawPad è possibile usare un DrawPad o un DrawPad3D.

Per aggiungere nuove figure geometriche sarà sempre necessario creare una nuova astrazione che ereditare dalla classe precedente.

Avendo disponibile una classe astratta Shape per un generico shape, è stato possibile derivare da essa classi concrete (Box, Circle, ecc…) che avessero la capacità di disegnarsi dato un SimpleDrawPad.

Le classi concrete possono essere istanziate per disegnarsi su un pad. Il dialogo con queste classi può avvenire anche attraverso uno Shape generico, e per ogni figura verranno utilizzate le specifiche caratteristiche per il disegno.

Progetto


Per rappresentare gli ADT si definiscono le classi:

DrawPad (Sorgente: pad\DrawPad.java)

DrawPad3D (Sorgente: pad\DrawPad3D.java)

Box (Sorgente: shape\Box.java)

Circle (Sorgente: shape\Triangle.java)

Triangle (Sorgente: shape\Triangle.java)

Penthagon (Sorgente: shape\Penthagon.java)

In più è stato creato un componente software che funge da libreria di funzioni.

Utility (Sorgente: util\Utility.java)
DrawPad:

La classe è definita all’interno del package pad “package pad;”

La signature “ public class DrawPad extends SimpleDrawPad ” esprime il fatto che DrawPad è una classe derivata dalla classe base SimpleDrawPad.

Ne eredita i dati e l’interfaccia, ma non i costruttori.



  • Il costruttore di default imposta i valori predefiniti del pad, cioè colore e pennello.

In oltre richiama il costruttore della classe base con la parola chiave “super()”.

Questo è necessario perché i costruttori non si ereditano, e solo il costruttore della classe base può sapere come inizializzare i dati che verranno ereditati.

public DrawPad()


I quattro parametri definiscono:

r => la componente rossa del colore

g => la componente rossa del colore

b => la componente rossa del colore

thickness => la dimensione della linea di disegno.

public DrawPad(float r, float g, float b, int thickness)



  • Metodo generale per il disegno di una scatola, accetta in ingresso le coordinate (x,y) del vertice superiore sinistro e di quello inferiore destro.

public void drawBox(int x0,int y0,int x3,int y3)

  • Metodo per il disegno di un quadrato, si devono passare come parametri le coordinate del vertice superiore sinistro e la lunghezza del lato.

public void drawSquare(int x, int y, int base)

  • Metodo per il disegno di un rettangolo, si devono passare come parametri le coordinate del vertice superiore sinistro, lunghezza del lato e della base.

public void drawRectangle(int x,int y, int base, int lato)

  • Metodo per il disegno di un cerchio, si devono passare come parametri le coordinate del centro e il raggio.

Public void drawCircle(int x, int y, int radius)

  • Metodo privato utilizzato per il calcolo delle coordinate dei vertici di un n-agono, si devono passare come parametri il numero dei lati e il raggio in cui si inscriverà il poligono.

private void vertex(int v, int r)

  • Metodo per il disegno di un triangolo equilatero, viene sfruttato il metodo “vertex”.

public void drawTriangle(int x, int y, int r)

  • Metodo per il disegno di un rombo, viene sfruttato il metodo “vertex”.

public void drawRhombus(int x, int y, int r)

  • Metodo per il disegno di un pentagono, viene sfruttato il metodo “vertex”.

public void drawPenthagon(int x, int y, int r)

  • Metodo per il disegno di una stella a cinque punte, viene sfruttato il metodo “vertex”.

public void drawStar(int x, int y, int r)

Per tutti i metodi che sfruttano “vertex” è necessario passare come parametri le coordinate e il raggio della circonferenza entro cui verranno tracciati.



DrawPad3D:

La classe è definita all’interno del package pad “package pad;”

Questa classe è derivata dalla classe DrawPad, che in questo caso funge da classe base,

per cui nel suo unico costruttore, quello di default public DrawPad3D(), dovrà invocare il costruttore della superclasse perché inizializzi tutti i dati che verranno ereditati.



Possiede un unico metodo, in quanto ho perso molto tempo nello studio per l’implementazione di una procedura che facesse linee tratteggiate

public void drawCube(int c, int base)

drawCube è un metodo che, dati in ingresso un angolo iniziale(il superiore sinistro della prima faccia) e la lunghezza della base, traccia un cubo tratteggiando gli spigoli invisibili.

La linea tratteggiate, meglio dire punteggiata, è implementata in un metodo privato, in quanto, potendo il metodo drawPoint ereditato da SimpleDrawPad, tracciare punti solo a coordinate intere, non può tracciare linee di punti se non sono verticali orizzontali o a 45°.

private void pointLine(int x0, int y0, int x1, int y1)

Box , Circle , Triangle , Penthagon

Sono tutte classi concrete derivate dalla classe astratta Sape, da cui ne ereditano i dati e l’interfaccia. Queste classi dovranno implementare un loro metodo di draw dichiarato nelle classe astratta Shape.

public class Box extends Shape

public class Circle extends Shape

public class Triangle extends Shape

public class Penthagon extends Shape

Sono definite tutte all’interno dello stesso package shape “package shape;”

Ogni costruttore dovrà anche provvedere a richiamare il costruttore della classe base, che memorizzerà il colore e il pennello usati dalla figura.

Ogni metodo di draw prende in ingresso come parametro un SimpleDrawPad che gli serve per potersi disegnare, in quanto le classi sanno solo come disegnarsi ma non ne hanno le capacità.

public void draw (SimpleDrawPad pad)

La signature di questo metodo è uguale per tutte le classi anche quelle future.
Utility:

Questo componente software mi serve come libreria di funzioni a cui l’oggetti Penthagon e successivi poligoni dovranno richiedere le coordinate dei loro vertici.

Per questo tutti i poligoni dovranno importare util.Utility.

Utility è definito nel package util “package util;”.

Per definirlo come classe statica ho posto la parola final nella signature della classe, che mi dice che questa classe non potrà dare origine a sottoclassi, poi ho chiamato static tutti i metodi che vi venivano definiti.

public final class Utility

public static void xVertex(int v, int r, int[] xpol)

public static void yVertex(int v, int r, int[] ypol)

I due metodi servono per ricavare le coordinate degli n vertici di un poligono, accettano come parametri in numero dei vertici, il raggio e un array passato per riferimento in cui depositeranno le coordinate ricavate.

Dettagli implementativi


DrawPad:

  • I metodi drawSquare e drawRectangle sfruttano il metodo drawBox.

public void drawSquare(int x, int y, int base){

drawBox(x,y,(x+base),(y+base));

}
public void drawRectangle(int x,int y, int base, int lato){

drawBox(x,y,(x+base),(y+lato));

}


  • Per il metodo di draw del cerchio ho utilizzato un ciclio for su 360° in combinazione di Math.sin() e Math.cos() per ricavarmi le coordinate dei punti in double, che ho poi convertito con un casting a interi, e le ho passate al metodo drawPoint.

public void drawCircle(int x, int y, int radius){

double

xp=0,


yp=0,

rad=0;


for(int a=0;a<360;a++)

{

rad=(a*Math.PI)/180; // escono radianti x sin & cos



xp=(radius*(Math.cos(rad))); // x della circonferenza

yp=(radius*(Math.sin(rad))); // y della circonferenza

drawPoint(x+(int)xp,y+(int)yp); //casting - drawPoint vuole int

}

}




  • Per calcolare i vertici dei poligoni ho seguito la stessa procedura, ma, per averli girati verso l’alto ho dovuto fare un cilclo da 270° a 630°.

Le coordinate trovate vengono depositate in due array privati definiti a livello di classe.
private void vertex(int v, int r){

xpol=new int[v];

ypol=new int[v];

int i=0;


for(int a=270;a<630;a=a+(360/v))

{

xpol[i]=(int)(r*(Math.cos((a*Math.PI)/180)));



ypol[i]=(int)(r*(Math.sin((a*Math.PI)/180)));

i++;


}

}


  • Il triangolo, il rombo e il pentagono sfruttano “vertex” recuperando poi le coordinate dei loro vertici dagli array. Tutte disegnano con un ciclo for n-1 lati, separatamente l’ultimo.

public void drawTriangle(int x, int y, int r){

vertex(3,r);

for(int i=0;i<2;i++)

drawLine(x+xpol[i],y+ypol[i],x+xpol[i+1],y+ypol[i+1]);

drawLine(x+xpol[2],y+ypol[2],x+xpol[0],y+ypol[0]);

}
public void drawRhombus(int x, int y, int r){

vertex(4,r);

for(int i=0;i<3;i++)

drawLine(x+xpol[i],y+ypol[i],x+xpol[i+1],y+ypol[i+1]);

drawLine(x+xpol[3],y+ypol[3],x+xpol[0],y+ypol[0]);

}

public void drawPenthagon(int x, int y, int r){



vertex(5,r);

for(int i=0;i<4;i++)

drawLine(x+xpol[i],y+ypol[i],x+xpol[i+1],y+ypol[i+1]);

drawLine(x+xpol[4],y+ypol[4],x+xpol[0],y+ypol[0]);

}


  • La drawStar no fa altro che richiedere a “vertex” i vertici di un pentagono, e poi calcolare quelli di un secondo pentagono capovolto, con lo stesso centro e il raggio ridotto al 30%.

public void drawStar(int x, int y, int r){

vertex(5,r);

int cx[]=new int [5];

int cy[]=new int [5];

int i=0;


for(int a=90;a<450;a=a+(72))

{

cx[i]=(int)(((r*30)/100)*(Math.cos((a*Math.PI)/180)));



cy[i]=(int)(((r*30)/100)*(Math.sin((a*Math.PI)/180)));

i++;


}

drawLine(x+xpol[0],y+ypol[0],x+cx[2],y+cy[2]);

drawLine(x+xpol[0],y+ypol[0],x+cx[3],y+cy[3]);

drawLine(x+xpol[1],y+ypol[1],x+cx[3],y+cy[3]);

drawLine(x+xpol[1],y+ypol[1],x+cx[4],y+cy[4]);

drawLine(x+xpol[2],y+ypol[2],x+cx[4],y+cy[4]);

drawLine(x+xpol[2],y+ypol[2],x+cx[0],y+cy[0]);

drawLine(x+xpol[3],y+ypol[3],x+cx[0],y+cy[0]);

drawLine(x+xpol[3],y+ypol[3],x+cx[1],y+cy[1]);

drawLine(x+xpol[4],y+ypol[4],x+cx[1],y+cy[1]);

drawLine(x+xpol[4],y+ypol[4],x+cx[2],y+cy[2]);

}
Penthagon:



  • In questo metodo di draw come negli altri si notano due righe di codice che servono alla figura per dire al pad passatogli per parametro di che colore è fatta e di che spessore sono i suoi lati.

pad.setCurrentColor(getColorRedComponent(),getColorGreenComponent(),getCol_orBlueComponent());

pad.setCurrentThickness(getThickness());
Per calcolare i vertici definisco due array uno per le coordinate x e uno per le y

//dichiarazioni degli array

int xpol[];

int ypol[];

//definizioni degli array

xpol=new int[5];

ypol=new int[5];
Richiamo le funzioni del componente Utility passandogli come parametro oltre al raggio e al numero dei vertici anche il riferimento agli array.

Utility.xVertex(5,cradius,xpol);

Utility.yVertex(5,cradius,ypol);

Successivamente disegno con un ciclo for n-1 lati, poi l’ultimo.


for(int i=0;i<4;i++)

pad.drawLine(cx+xpol[i],cy+ypol[i],cx+xpol[i+1],cy+ypol[i+1]);

pad.drawLine(cx+xpol[4],cy+ypol[4],cx+xpol[0],cy+ypol[0]);

Casi d’uso

Con il componente software TestPad creo un oggetto di tipo DrawPad e gli invio messaggi per disegnare tutte le figure che conosce, quindi anche quelle di SimpleDrawPad, con vari colori e dimensioni del pennello.
import pad.DrawPad;

public class TestPad{

public static void main(String args[]){

DrawPad pad = new DrawPad(0,0,0,7);

pad.setCurrentColor(0,0,1);

pad.drawPoint(290,250);//definito in SimpleDrawPad

pad.setCurrentThickness(1);

pad.drawLine(200,150,300,10); //definito in SimpleDrawPad

pad.drawSquare(50,50,50);

pad.setCurrentColor(1,0,1);

pad.setCurrentThickness(2);

pad.drawBox(10,210,157,245);

pad.setCurrentColor(0,0,1);

pad.drawRectangle(90,150,70,30);

pad.setCurrentColor(0,1,0);

pad.drawCircle(75,75,25);

pad.setCurrentColor(0,1,0.5f);

pad.setCurrentThickness(4);

pad.drawTriangle(290,70,50);

pad.drawRhombus(20,50,20);

pad.setCurrentColor(1,0.4f,0.4f);

pad.drawPenthagon(170,70,50);

pad.drawStar(290,250,100);

pad.setCurrentThickness(4);

for(int i=0;i<35;i++)

pad.drawCircle(75,310,i);

pad.show();

try {


Thread.currentThread().sleep(9000);

}

catch (Exception ex){



}
// exit

System.exit(0);

}

}
N
ell’immagine sottostante è riportato l’output di TestPad.



Con il componente software TestPad3D creo un oggetto di tipo DrawPad3D e gli invio messaggi per disegnare un cubo.
import pad.DrawPad3D;

public class TestPad3D{

public static void main(String args[]){

DrawPad3D pad=new DrawPad3D();

pad.setCurrentColor(1,0.5f,0);

pad.drawCube(10,40);

pad.show();

try {


Thread.currentThread().sleep(3000);

}

catch (Exception ex){



}
// exit

System.exit(0);

}

}
N
ell’immagine sottostante è riportato l’output di TestPad3D.


Con il componente software TestShape creo oggetti Box, Circle ecc… , tutti referenziati tramite lo Shape generico. Le figure sono disegnate su due pad differenti.


import tools.*;

import shape.*;

public class TestShape{

public static void main(String args[]){

SimpleDrawPad pad0=new SimpleDrawPad();

Shape shape0 = new Box(190,190,150,200,0.5f,1,0,3);

Shape shape2 = new Triangle(10,45,20,88,95,156,0.5f,1,0,3);

Shape shape3 = new Penthagon(0,0,0,3);

shape0.draw(pad0);

shape2.draw(pad0);

shape3.draw(pad0);
SimpleDrawPad pad1=new SimpleDrawPad();

Shape shape1 = new Circle(0.5f,1,0,6);

Shape shape4 = new Penthagon(50,75,30,1,0,0,3);

shape1.draw(pad1);

shape4.draw(pad1);

pad0.show();

try {

Thread.currentThread().sleep(1000);



}

catch (Exception ex){

}

//pad0.hide(); è possibile anche nascondere un pad



pad1.show();

try {


Thread.currentThread().sleep(5000);

}

catch (Exception ex){



}
// exit

System.exit(0);

}

}
N
ell’immagine sottostante è riportato l’output di TestShape.







Concetti e tecniche acquisiti

Durante lo sviluppo del progetto DrawPad e DrawPad3D sono stati utilizzati i concetti dell’ereditarietà. Entrambe possono essere utilizzate dove si usa un SimpleDrawPad, e DrawPad3D anche dove si usa un DrawPad, essendo derivata da essa.

Ereditando dalla classe base le derivate possiedono la stessa interfaccia e gli stessi dati, se definiti con visibilità protected, ma non i costruttori.

I costruttori sono personali per ogni classe e non si ereditano.

Ogni istanza della classe derivata comprende in se indirettamente un oggetto base, ed è per questo che non si può prescindere dal costruirlo.

I dati non possono essere inizializzati dalla classe derivata perché non può conoscere totalmente la classe base anche se la eredita, ma solo il costruttore della classe base può sapere come inizializzare correttamente i dati che verranno ereditati.

ognuno deve costruire quello che gli compete” (da cap “Ereditarietà e Costruttori” lucidi del professore)

Se ad un oggetto derivato arrivano messaggi appartenenti alla sua interfaccia ma a cui non è in grado di rispondere, in quanto non possiede l’implementazione(es: metodi ereditati), i messaggi verranno riversati sull’oggetto base che sicuramente sarà in grado di rispondere. Questo accade nell’esempio TestPad quando ad un DrawPad si richiede di disegnare delle linee e dei punti.

Una limitazione al costruire un pad usando l’ereditarietà può risultare dal fatto che, per aggiungere nuove figure si è costretti a derivare la classe precedente, oppure anche se poco ortodosso modificare quella esistente.

Fatto questo saranno disponibili nuove figure, pero il cliente dovrà studiarne la documentazione per capirne il funzionamento.

A questo si può porre rimedio utilizzando le classi astratte come Shape che dichiara con abstract tutti i metodi che le classi derivate devono implementare.

Infatti ogni ADT “figura” dovrà implementare un proprio metodo draw (), oltre a costruire col metodo super(); l’oggetto base per il motivo sopra riportato.

Usando le astrazioni si crea una gerarchia in cui ogni figura è anche uno Shape, e tramite il polimorfismo un cliente può dialogare semplicemente con uno shape generico inviando messaggi di draw senza preoccuparsi di che figura geometrica si tratta, sarà compito dell’oggetto referenziato da Shape rispondere correttamente.

Nel progettare tutte le classi ho cercato di utilizzare il concetto di package per capirene meglio in comportamento nelle classe, nel progetto intero e durante la compilazine.



Infatti per compilare tutto correttamente è stato necessario impostare un CLASSPATH corrispondente alla directory in cui si trovano tutti i package.

Ho iniziato anche a costruire un componente software (classe statica)come libreria di funzioni riutilizzabile secondo le necessità.


Condividi con i tuoi amici:


©astratto.info 2019
invia messaggio

    Pagina principale