© 1999-2003, Flemming Koch Jensen
Alle rettigheder forbeholdt
Prototype Pattern
Vejledende løsninger

 

 

1 Klassediagrammet bliver (uden set-konstruktorer og toString):
Figur 1:
Klasse-diagram
Prototype interfacet er som det plejer:
interface Prototype {
  
  public Prototype Clone();
}

ProtoInt er ukompliceret:

class ProtoInt implements Prototype {
  private int value;
  
  public ProtoInt( int i ) {
    value = i;
  }
  
  public ProtoInt( ProtoInt p ) {
    value = p.value;
  }
  
  public String toString() {
    return "[ProtoInt: value=" + value + "]";
  }
  
  public Prototype Clone() {
    return new ProtoInt( this );
  }
}
  Den næste Prototype-klasse adskiller sig kun minimalt fra den foregående:
 
class ProtoString implements Prototype {
  private String value;
  
  public ProtoString( String s ) {
    value = s;
  }
  
  public ProtoString( ProtoString p ) {
    value = p.value;
  }
  
  public String toString() {
    return "[ProtoString: value=\"" + value + "\"]";
  }
  
  public Prototype Clone() {
    return new ProtoString( this );
  }
}
  I klienten er der intet konkret kendskab til de to Prototype-klasser, idet alle referencer er af klassen Prototype:
 
class Client {
  private Prototype proto;
  
  public Client( Prototype p ) {
    proto = p;
  }
  
  public void operation() {
    Prototype p1 = proto.Clone();
    Prototype p2 = proto.Clone();
    
    System.out.println( proto );
    System.out.println( p1 );
    System.out.println( p2 );
  }
}
  Dernæst har vi test-anvendelsen, hvor vi har det konkrete kendskab til de to Prototype-klasser
class TestPrototype {

  public static void main( String argv[] ) {
    
    Prototype p1 = new ProtoInt( 5 );
    Prototype p2 = new ProtoString( "fem" );
    
    Client c1 = new Client( p1 );
    Client c2 = new Client( p2 );
    
    c1.operation();
    c2.operation();
  }
}

[PrototypeInt: value=5]
[PrototypeInt: value=5]
[PrototypeInt: value=5]
[PrototypeString: value="fem"]
[PrototypeString: value="fem"]
[PrototypeString: value="fem"]

Kildetekster:

Prototype.java
ProtoInt.java

ProtoString.java
Client.java
TestPrototype.java

 

2 Prototype interfacet er stadig nødvendigt, hvis vi vil bibeholde klientens abstrakte kendskab til objekterne. Vi lader det nedarve fra Cloneable og ændre signaturen for vores klone-metode:
interface Prototype extends Cloneable {
  
  public Object clone();
}
Forskellen i subklasserne ligger i implementationen af clone:
class ProtoInt implements Prototype {
  private int value;
  
  public ProtoInt( int i ) {
    value = i;
  }
  
  public ProtoInt( ProtoInt p ) {
    value = p.value;
  }
  
  public String toString() {
    return "[ProtoInt: value=" + value + "]";
  }
  
  public Object clone() {
    try {
      return super.clone();
    }
    catch ( CloneNotSupportedException e ) {
      return null;
    }
  }
}
Vi laver try-catch selvom der aldrig kan komme en exception (da vi implementer Cloneable) - for det skal vi!

Som før, er ProtoString meget lig ProtoInt:

class ProtoString implements Prototype {
  private String value;
  
  public ProtoString( String s ) {
    value = s;
  }
  
  public ProtoString( ProtoString p ) {
    value = p.value;
  }
  
  public String toString() {
    return "[ProtoString: value=\"" + value + "\"]";
  }
  
  public Object clone() {
    try {
      return super.clone();
    }
    catch ( CloneNotSupportedException e ) {
      return null;
    }
  }
}
Dernæst har vi klienten:
class Client {
  private Prototype proto;
  
  public Client( Prototype p ) {
    proto = p;
  }
  
  public void operation() {
    Object p1 = proto.clone();
    Object p2 = proto.clone();
    
    System.out.println( proto );
    System.out.println( p1 );
    System.out.println( p2 );
  }
}
Bemærk hvor lidt klienten bliver berørt af vores refactoring - der er kun to mindre forskelle!
clone-metoden skal staves med lille for at anvende Java's understøttelse (Den eneste grund til at vi stavede det med stort i opgave 1, var for at undgå at kollidere med Java's understøttelse!).

Den anden forskel er at clone-metoden nu ikke er så typestærk, idet den ikke kan returnere en Prototype, men vi må nøjes med et Object, der normalt må castes (Vi undgår det dog her, da vi ikke anvender andet end toString-metoden)

I testanvendelsen har vi placeret instantieringen af de to objekter i selve kaldet til klientens konstruktor. Det er gjort for at undgå konkrete referencer (ikke at det betyder det store).
class TestPrototype {

  public static void main( String argv[] ) {
    
    Client c1 = new Client( new ProtoInt( 5 ) );
    Client c2 = new Client( new ProtoString( "fem" ) );
    
    c1.operation();
    c2.operation();
  }
}

[ProtoInt: value=5]
[ProtoInt: value=5]
[ProtoInt: value=5]
[ProtoString: value="fem"]
[ProtoString: value="fem"]
[ProtoString: value="fem"]

Kildetekster:

Prototype.java
ProtoInt.java

ProtoString.java
Client.java
TestPrototype.java