Avatar billede stonejunkie Nybegynder
27. januar 2006 - 16:31 Der er 19 kommentarer og
1 løsning

Tilgå klasse gennem et interface.

Hej jeg håber der er nogen der kan hjælpe mig.

Jeg har et Person objekt som jeg gerne vil have gemt i en database og det kan også godt lade sig gøre. Ved den nedenstående kode:

-- Person.java --
public class Person {

    private String name;
    private String cpr;
   
    public Person(String name, String cpr){
        this.name = name;
        this.cpr = cpr;
       
    }
   
    public String getName(){
        return this.name;
    }
   
    public String getCpr(){
        return this.cpr;
    }
   
/** Det er denne metode det hele handler om **/
    public void save(){
        PersonSQL saveP = new PersonSQL();
        saveP.save(this);
    }
}


-- PersonSQL.java --
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class PersonSQL implements IPersonIO{

    private Connection con;
    private PreparedStatement prep;
    private String sql;
   
    public PersonSQL(){
        con = JDBCConnection.getInstance().getConnection();
       
    }
   
    public void save(Person p){
        sql = "INSERT INTO Person (name, cpr) VALUES (?, ?)";
        try {
            prep = con.prepareStatement(sql);
            prep.setString(1, p.getName());
            prep.setString(2, p.getCpr());
            prep.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

-- IPersonIO.java --
public interface IPersonIO {
    public abstract void save(Person p);
}


Jeg vil dog gerne have en løsning hvor PersonSQL implementer IPersonIO interfacet. Derudover ville jeg gerne kunne ændre save() metoden i Person.java til noget lignende:

public void save(){
    IPersonIO.save(this);
}

Så jeg ikke kalder PersonSQL men kun interfacet. Grunden til at jeg gerne vil have denne løsning er at hvis jeg på et andet tidspunkt hellere ville gemme objektet i en fil så kan jeg blot udskrifte PersonSQL med PersonGemObjektIFil som ligeledes skulle implentere IPersonIO.

Skriv endelig hvis jeg har været uklar i min problembeskrivelse.

Jeg håber der er nogen der kan hjælpe mig med at få lavet løsningen, for det har ikke kunne lykkedes for mig.
Avatar billede mikkelbm Nybegynder
27. januar 2006 - 17:04 #1
I mine øjne er din opbygning næsten rigtig som den er nu. Jeg ville dog ikke have en save metode på Person-klassen, da det giver en høj kobling til din IPersonIO implementation.
Avatar billede mikkelbm Nybegynder
27. januar 2006 - 17:06 #2
Hvis du endelig VIL ha' Person-klassen til at have ansvaret for at gemme, kan du smide en IPersonIO implementation med i constructoren på din Person-klasse. Men umiddelbart bryder jeg mig ikke om den løsning.
Avatar billede mikkelbm Nybegynder
27. januar 2006 - 17:08 #3
Men det du efterspørger er jo hvordan du kører på interfacet og ikke på PersonSQL. Det gør du simpelthen ved at:

IPersonIO personIO = new PersonSQL ();
personIO.save (p);

Så er det på interfacet og ikke på klassen PersonSQL.
Avatar billede mikkelbm Nybegynder
27. januar 2006 - 17:10 #4
Når jeg nærlæser dit spørgsmål, så forstår jeg det sådan, at det du efterlyser er hvordan du kan køre på interfacet og kun på interfacet?

Det kan du ikke. Der skal altid oprettes en instans af en klasse der implementerer dit interface. Så du kan ikke undgå: new PersonSQL(); eller andre klasser der implementerer interfacet.
Avatar billede stonejunkie Nybegynder
27. januar 2006 - 18:05 #5
Ok tak for svaret det var rart at få det afklaret så jeg ikke bruger mere tid på det. Hvis du smidder et svar så skal du få dine point.

Igen tak for hjælpen.
Avatar billede mikkelbm Nybegynder
27. januar 2006 - 18:09 #6
Var så lidt. Du spørger bare...
Avatar billede stonejunkie Nybegynder
27. januar 2006 - 18:11 #7
Jeg kan desværre ikke lige få forbindelse til min database nu men jeg tænkte på om dette kunne være en fornuftig løsning og i denne løsning har jeg inddraget mikkelbm's forslag om at Person ikke selv skal gemme objektet. Men som sagt har jeg ikke kunne kontrollere om det virker.

-- Controller.java --
import java.util.ArrayList;
import java.util.Iterator;

public class Controller {

    private ArrayList personSavers;
    private static Controller ctrl;
   
    private Controller(){
       
    }
   
    public static Controller getInstance(){
        if (ctrl == null){
            ctrl = new Controller();
        }
       
        return ctrl;
    }
   
    public void addSaver(IPersonIO saver){
        personSavers.add(saver);
    }
    public void save(Person p){
       
          Iterator i = personSavers.iterator();
        while(i.hasNext()){
            IPersonIO saver = (IPersonIO) i.next();
            saver.save(p);
          }
    }

}


-- Person.java --
public class Person {

    private String name;
    private String cpr;
   
    public Person(String name, String cpr){
        this.name = name;
        this.cpr = cpr;
       
    }
   
    public String getName(){
        return this.name;
    }
   
    public String getCpr(){
        return this.cpr;
    }

}


-- PersonSQL.java --
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class PersonSQL implements IPersonIO{

    private Controller ctrl;
    private Connection con;
    private PreparedStatement prep;
    private String sql;
   
    public PersonSQL(){
        con = JDBCConnection.getInstance().getConnection();
        ctrl = Controller.getInstance();
        ctrl.addSaver(this);
       
    }
   
    public void save(Person p){
        sql = "INSERT INTO Person (name, cpr) VALUES (?, ?)";
        try {
                prep = con.prepareStatement(sql);
            prep.setString(1, p.getName());
            prep.setString(2, p.getCpr());
            prep.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


-- IPersonIO.java --
public interface IPersonIO {
    public abstract void save(Person p);
}
Avatar billede mikkelbm Nybegynder
27. januar 2006 - 18:18 #8
Det ser rigtig fornuftigt ud!
Avatar billede stonejunkie Nybegynder
27. januar 2006 - 18:28 #9
Det er vel meget smart at man både kan tilføje en klasse der gemmer objektet i databasen og en klasse der gemmer samme objekt i en fil, så man har en backup hvis netforbindelsen smutter. Men så kan jeg ikke lige gennemskue hvordan man henter alle personer fra databasen/filen uden at få dobbelt poster da begge klasser jo vil blive hente person objekterne.
Avatar billede mikkelbm Nybegynder
27. januar 2006 - 18:42 #10
Efter min mening er det en god idé at holde sig til én måde at gemme sine data på. Fordelen ved interfacet er at du kan skifte til at gemme på disken, hvis du af en eller anden grund ikke længere kan bruge databasen. Med det mener jeg ikke, at du i 5 minutter ikke kan bruge database, men at måden hvorpå du gemmer dine objekter laves om til at bruge disken fremover.

Håber du forstår.

Det kan også være at der er nogen der kan give en god og fornuftig grund til at gemme begge steder - jeg kan bare ikke umiddelbart.
Avatar billede jakoba Nybegynder
27. januar 2006 - 19:38 #11
Det forekommer lidt naturstridig med et interface der specifikt refererer (i parameteren) til den klasse det skal være interface for. Hvad er så fidusen ved overhovedet at have et interface der ?
Avatar billede mikkelbm Nybegynder
27. januar 2006 - 19:41 #12
Interfacet skjuler vel implementationen af hvordan man ønsker sit objekt gemt.
Men ja, man kunne sagtens lave et interface, som Person skal implementere for at kunne bruge save metoden - hvis det er det du tænker på?
Avatar billede stonejunkie Nybegynder
27. januar 2006 - 20:19 #13
jakoba, jeg er ikke helt med. Kan du ikke vise hvor i koden det er.

Tak for dit input.
Avatar billede jakoba Nybegynder
28. januar 2006 - 03:27 #14
klassen Person er parameter i interfacet
public interface IPersonIO {
    public abstract void save(Person p);
                                      ^^^^^^^^
}

Interfacet bruges til at definere klassen Person ....
**************  Ups nej den gør ikke, den bruges til at definere subtyper til klassen Person.

men jeg forstår stadig ikke rigtig hvorfor der er brug for et interface der.
Avatar billede mikkelbm Nybegynder
28. januar 2006 - 10:47 #15
>> Jakoba

"Interfacet bruges til at definere klassen Person ....
**************  Ups nej den gør ikke, den bruges til at definere subtyper til klassen Person."


Hvor i alverden læser du det henne?
Avatar billede jakoba Nybegynder
28. januar 2006 - 18:29 #16
Du har ret, jeg vrøvler her :(
Avatar billede stonejunkie Nybegynder
29. januar 2006 - 14:55 #17
Nu har jeg siddet og kigget nærmere på min egen løsning. Jeg løber bare ind i problemet med at der på nuværende tidspunkt ikke er nogen af klasserne der opretter en instans af PersonSQL klassen. Hvordan løser jeg dette problem så løsningen stadig forbliver løst koblet? Hvis jeg giver Controller ansvaret så er jeg jo tilbage hvor jeg startede.

Tak for jeres svar.
Avatar billede mikkelbm Nybegynder
29. januar 2006 - 16:06 #18
Jeg vil mene at det er din controllers ansvar at oprette instansen er PersonSQL klassen. Og du er ikke tilbage hvor du startede, da du fra starten havde lagt ansvaret inde i Person-klassen.

Og hvis vi skal tage lidt udgangspunkt i Jakobs kommentar, om at et interface ikke er nødvendigt i dette tilfælde, kan vi gøre følgende for at et interface netop er på sin plads her.

Hvis vi tænker den lidt længere. I øjeblikket har du en høj kobling, da du i interfacet tager Person-klassen med som parameter. Dette kan løses ved at lade Person nedarve fra et interface dedikeret til dette - og hvis vi så tager et skridt yderligere, kan du faktisk lave starten på dit eget persistens-framework.

Et lille hurtigt udkast:

// Først et interface som alle objekter der skal persisteres/gemmes skal nedarve fra
// Jeg har lavet en metode der returnerer et id, da man i mange tilfælde vil ha' brug
// for dette i forbindelse med database. Du kan undlade den i første omgang,
// hvis ikke du mener der er relevant
public interface PersistencyObject
{
    public int getId ();
}


// Nu kommer så dit eget interface (omdøbt så det ikke indikerer direkte at det har med Person at gør)
// - nu med et PersistencyObject som parameter
public interface ISaver
{
    public void save(PersistencyObject obj);
}


// Ovenstående er så dit eget lille framework, der udbyder nogle interfaces der kan bruges til at gemme objekter.
// Nu kommer så implementationen:


// Implementation af din Person-klasse
public class Person implements PersistencyObject
{
  private int id;

  public Person (int id)
  {
      this.id = id;
  }

  public int getId ()
  {
      return id;
  }

  // Og alle de andre metoder du har på dit Person-objekt
}


// Implementation af din PersonSQL
public class PersonSQL implements ISaver
{
  public void save(PersistencyObject p)
  {
      if (p instanceof Person)
      {
        Person person = (Person)p;
        // Gem dit objekt i databasen
      }
  }

  // eventuelle andre hjælpemetoder...
}


Ved ovenstående har du en klar adskillelse og løst koblet system.
Din controller skal stadig ha' ansvaret for at oprette en instans af PersonSQL klassen, men det er i mine øjne også det helt rigtige sted at gøre det.


Håber du kan bruge det. Spørg bare hvis du er i tvivl om noget.
Avatar billede stonejunkie Nybegynder
29. januar 2006 - 16:51 #19
Tak for dit meget brugbare svar.

Jeg formoder at du vil oprette PersonSQL instansen i Controller klassen med denne linje: ISaver PersonSaver = new PersonSQL();?

Nu er mit spørgsmål så om det er nødvendigt at kontrollere om PersistencyObject objektet er et Person objekt? Ville dette ikke kun være nødvendigt hvis jeg havde en general SQL klasse som gemmer forskellige PersistencyObject objekter?
f.eks.:
if (p instanceof Person)
  {
    Person person = (Person)p;
    // Gem Person objekt i databasen
  }
if (p instanceof Dyr)
  {
    Dyr dyr = (Dyr)p;
    // Gem Dyr objekt i databasen
  }

Tak for svaret.
Avatar billede mikkelbm Nybegynder
29. januar 2006 - 16:56 #20
"Jeg formoder at du vil oprette PersonSQL instansen i Controller klassen med denne linje: ISaver PersonSaver = new PersonSQL();?"

- Lige præcis

Og til dit andet spørgsmål, så kan du faktisk godt have lidt ret i det. Det var blot en sikkerhedsforanstaltning, så du ikke vil få en InvalidCastException, når du prøver at caste til et Person-objekt.
vil du kunne gemme et andet objekt, f.eks. 'Dyr', så vil jeg anbefale at du laver en klasse/mapper til det, da det hurtigt bliver noget rod med at gemme forskellige ting i samme klasse. Man kunne også sagtens forestille sig, at du også vil hente objekterne igen, og derfor smider en ny metode på interfacet - det vil være meget nemmere at implementere, hvis ansvaret er fordelt på forskellige klasser, der ved præcist hvordan de forskellige objekter skal gemmes.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester