Adaptor-Pattern mit Ocaml's Module System (Re: [linux-l] Ocml vs. Java)

Oliver Bandel oliver at first.in-berlin.de
Fr Sep 23 08:26:01 CEST 2005


On Thu, Sep 22, 2005 at 05:07:06PM +0200, Volker Grabsch wrote:
> On Wed, Sep 21, 2005 at 11:49:05PM +0200, Oliver Bandel wrote:
> > OK, dann habe ich mich vielleicht ungünstig/mißverständlich ausgedrückt.
> > 
> > Eine Sprache nennt man funktional, wenn man Funktionen GENAUSO wie
> > andere Values behandeln kann. Tricksereien gelten nicht.
> 
[...]


> > BTW: Adaptor-Pattern ist auch sehr interessant.
> > Im Pather-Buch (Perl) haben die das ja auch mal vorgeführt.
> > 
> > Nach o.g. Buche steht da unter Intent:
> >   "Convert the interface of a class into another interface clients expect.
> >    Adapter lets classes wpork together that couldn't otherwise because of
> >    incompatible intefaces."
> > 
> > Hmhh, mach' mir jetzt nicht die Mühe, den ganzen Kram zu lesen;
> > schaue mir im Panther-Buch die Grafik an und würde wieder sagen, daß OCaml's
> > Modulsystem einem hier hilft.
> > Ein Interface und mehrere spezialisierte Implementierungen. Das riehct nach
> > Anwendung von Funktoren.
> 
> Nee, Funktoren helfen da nicht.

Och, vielleicht würden die da sogar helfen,
aber man braucht sie noch nicht einmal.

Das Modulsystem ganz einfach mit Structure/Signature und
Module types reicht schon aus.


> Es geht darum, dass die
> Implementierungen einfach mal andere Interfaces habe, d.h. die
> Klassen, Funktionen, etc. völlig anders heißen, vielleicht sogar
> die Art, wie diese Funktionen zusammenarbeiten, anders ist.

Es geht aus Sicht des Adaptors darum, EIN Interface für verschiedene
andere Sourcen zu haben, das einen Adapter darstellt.

Klinke <-> CINCH
Klinke <-> BNC
Klinke <-> Banane

alles Klinke, oder was? ;-)

OK, mal ne Klinke bauen:



> 
> Und was du dann machst, ist auch klar: Du suchst dir ein eigenes
> Interface, und schreibst einen kleinen "Wrapper". Das ist auch in
> OO-Sprachen überhaupt kein Ding.

Selbst in OO-Sprachen geht das also. Donnerwetter. ;-)

Na, dann erübrigt sich das folgende ja fast... aber nun bringe ich das
noch zu Ende. :)


> Es ist mehr ein notwendiges Übel,
> und das Muster sagt nur, dass du's einfach machen sollst, statt deinen
> gesamten Code auf das fremde Interface umzumuddeln.

Eben.


Na, dann woll'n wa mal:


==================================================================================
(* ----------------------------------------------------------------- *)
(* Third Party Modules or something you don't want to change anymore *)
(* ----------------------------------------------------------------- *)
module A = 
  struct
    let print_dieses () = print_endline "Hallo, dies ist Variante A!"
    let hide_me () = print_endline "Hide me!" (* not hidden yet *)
  end

module B = 
  struct
    let print_jenes () = print_endline "Hallo, dies ist Variante B!"
    let hide_me_too () = print_endline "Hide me!" (* not hidden yet *)
  end

module C = 
  struct
    let print_noch_eins () = print_endline "Hallo, dies ist Variante C!"
    let hide_me_please () = print_endline "Hide me!" (* not hidden yet *)
  end


(* ----------------------------------------------------------------- *)
(* Now do the adaption                                               *)
(* ----------------------------------------------------------------- *)
module A_unified =
  (struct
    include A
    let print = print_dieses
      (* and more adapter code if necessary *)
  end
   :
  sig
    val print : unit -> unit
  end)

module B_unified =
  (struct
    include B
    let print = print_jenes
      (* and more adapter code if necessary *)
  end
   :
  sig
    val print : unit -> unit
  end)

module C_unified =
  (struct
    include C
    let print = print_noch_eins
      (* and more adapter code if necessary *)
  end
   :
  sig
    val print : unit -> unit
  end)


(* ----------------------------------------------------------------- *)
(* Now: use the adaptees                                             *)
(* ----------------------------------------------------------------- *)

let _ = A_unified.print();
        B_unified.print();
        C_unified.print()

==================================================================================


mal schauen, was das Interface des adaptor.ml ist:
------------------------------------------------------
first:/tmp oliver$ ocamlc -i adaptor.ml 
module A : sig val print_dieses : unit -> unit val hide_me : unit -> unit end
module B :
  sig val print_jenes : unit -> unit val hide_me_too : unit -> unit end
module C :
  sig
    val print_noch_eins : unit -> unit
    val hide_me_please : unit -> unit
  end
module A_unified : sig val print : unit -> unit end
module B_unified : sig val print : unit -> unit end
module C_unified : sig val print : unit -> unit end
==================================================================================

Und ausgeführt:
---------------

===========================================
first:/tmp oliver$ ocaml adaptor.ml 
Hallo, dies ist Variante A!
Hallo, dies ist Variante B!
Hallo, dies ist Variante C!
first:/tmp oliver$ 
===========================================


Wie man sieht sind die Module alle selben Typs.
Adaption gelungen. :)

Kein OO notwendig.
Design Pattern mit Modul System erschlagen.
Kein besonderer Aufwand.

Man kann sich übrigens viel Tipparbeit sparen (und auch
Fehler bei den Moduls-Dignature, wenn man module types
nutz.

Das kommt in einer anderen Mail, wo es um die Bridge geht. :)


Ciao,
   Oliver



Mehr Informationen über die Mailingliste linux-l