Adapter Muster: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
(Die Seite wurde neu angelegt: =Konzept= Das Adapter Muster ist, wie der Name schon sagt, ein Muster, welches als ein Adapter zwischen 2 Objekten fungiert.<br> Diese Muster ist sehr nützlich bei dem...)
 
K (Der Ausdruck ''<cpp>(.*?)</cpp>'' wurde ersetzt mit ''<source lang="cpp">$1</source>''.)
 
(2 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
 
=Konzept=
 
=Konzept=
Das Adapter Muster ist, wie der Name schon sagt, ein Muster, welches als ein Adapter zwischen 2 Objekten fungiert.<br>
+
Das Adapter Muster ist, wie der Name schon sagt, ein Muster, welches als ein Adapter zwischen 2 Objekten fungiert. Diese Muster ist sehr nützlich bei dem flexiblen Programmieren von ressourcenabhängigen Managern.
Diese Muster ist sehr nützlich bei dem flexiblen programmieren von Ressourcenabhängigen Managern.<br>
+
 
Das Konzept des Adapters ist es Daten, die in einen Format vorliegen durch den Adapter in ein für die andere nutzbare Form zu bringen.<br>
+
Das Konzept des Adapters ist es, Daten, die in einen Format vorliegen, durch den Adapter in ein für die andere nutzbare Form zu bringen. So kann ein Adapter z.B. Bitmaps lesen und diese in das benötigte Format umwandeln, ein anderer Adapter kann dies für ein PNG und ein weiterer kann dies z.B. für eine AVI.
So kann ein Adapter z.B. Bitmaps lesen und diese in das gebrauchte Format umwandeln, ein anderer Adapter kann dies für ein PNG und ein weiteres kann dies für eine AVI.
+
 
 
=Umsetzung=
 
=Umsetzung=
Wir hatten gerade von meheren möglichen Adaptern geredet, die verschiedene Bildformate und Bildsequenzformate lesen sollen.<br>
+
 
Also sollten wir erstmal ermöglichen, dass man mehere Adapter, mit jeweils einen Dateiformat verbinden kann.<br>
+
== Skizze ==
 +
 
 +
Wir haben gerade von mehren möglichen Adaptern geredet, die verschiedene Bildformate und Bildsequenzformate lesen sollen. Also sollten wir erstmal ermöglichen, dass man mehrere Adapter mit jeweils einen Dateiformat verbinden kann.
 +
 
 +
 
 
'''C++'''
 
'''C++'''
<cpp>class TAdapter
+
<source lang="cpp">class TAdapter
 
{
 
{
 
   public:
 
   public:
Zeile 24: Zeile 28:
 
     TAdapter* GetAdapterInstance(string ExtensionName);
 
     TAdapter* GetAdapterInstance(string ExtensionName);
 
     bool RemoveAdapter(string ExtensionName);
 
     bool RemoveAdapter(string ExtensionName);
};</cpp>
+
};</source>
 +
 
 +
 
 
'''Pascal'''
 
'''Pascal'''
<pascal>pTAdapter=^TAdapter;
+
<source lang="pascal">TAdapter = class
TAdapter=class
 
 
   public:
 
   public:
     constructor Create(FileName:string);
+
     constructor Create(FileName: String);
 +
end;
 +
 
 +
(*
 +
Anmerkung: Objekt Pascal kennt die Möglichkeit class of class und diese kann man anstatt eines Callbacks verwenden.
 +
Nutzen tut man dieses dann so:
 +
type
 +
  TAdapterCls = class of TAdapter;
 +
var
 +
  AdapterCls: TAdapterCls;
 +
  MyAdapter: TAdapter;
 +
begin
 +
  MyAdapter := AdapterCls.Create();
 
end;
 
end;
 +
*)
  
//Anmerkung: Objekte Pascal kennt die Möglickeit class of class und diese kann man anstatt eines Callbacks verwenden.
+
CreateAdapterInstanceCallback = function (FileName: String): TAdapter;
//          Nutzen tut man dieses dann so. var AdapterCls:TAdapterCls; MyAdapter:TAdapter; begin MyAdapter:=AdapterCls.Create(); end;
 
//TAdapterCls=class of TAdpater;
 
CreateAdapterInstanceCallback=function (FileName:string):pTAdapter;
 
  
TAdapterManager=class
+
TAdapterManager = class
 
   protected:
 
   protected:
     RegisteredAdapters:TUniqueHashList;//Eine Liste in der kein Wert doppelt vorkommt und die Strings durch Hashes im Zugriff optimiert werden.
+
     RegisteredAdapters: TUniqueHashList; //Eine Liste in der kein Wert doppelt vorkommt und die Strings durch Hashes im Zugriff optimiert werden.
 
   public:
 
   public:
     //procedure RegisterAdapter(ExtensionName:string; AdapterCls:TAdapterCls);
+
     procedure RegisterAdapter(ExtensionName: String; Callback: CreateAdapterInstanceCallback);
    procedure RegisterAdapter(ExtensionName:string; Callback:CreateAdapterInstanceCallback);
+
     function GetAdapterInstance(ExtensionName: String): TAdapter:
     function GetAdapterInstance(ExtensionName:string):pTAdapter:
+
     function RemoveAdapter(ExtensionName: String): Boolean;
     function RemoveAdapter(ExtensionName:string):Boolean;
+
end;</source>
end;</pascal>
+
Jetzt können wir einen eigenen Adapter dem Adaptermanager bekannt machen, indem wir die Extension(z.B. "gz" oder "avi") und ein passenden Callback übergeben. Der Callback besitzt nur eine Zeile Code, denn er erzeugt nur eine Instanz und gibt diese zurück.
Jetzt können wir Einen eigenen Adapter dem AdapterManager bekannt machen, indem wir die Extension(z.B. "gz" oder "avi") und ein passenden Callback übergeben.<br>
+
 
Der Callback besitzt nur eine Zeile Code, denn er erzeugt nur eine Instanz und gibt diese zurück.<br>
+
== Abstrahierung ==
<br>
+
 
Nun können wir Adapter registrieren, erstellen und entfernen aber was ist mit dem Austausch von Daten?<br>
+
Nun können wir Adapter registrieren, erstellen und entfernen, aber was ist mit dem Austausch von Daten?
Da der Adapter wissen muss, was der Empfänger für Daten haben will, muss der Adapter für den Empfänger zugeschnitten werden und der Empfängger braucht auch eine Routine, die die Daten vom Adapter entgegen nimmt und verarbeitet.<br>
+
 
Also müssen wir die Klassen möglichst abstrakt bauen und die Verarbeitung den Nachfahren überlassen.<br>
+
Da der Adapter wissen muss, was der Empfänger für Daten haben will, muss der Adapter für den Empfänger zugeschnitten werden und der Empfänger braucht auch eine Routine, die die Daten vom Adapter entgegen nimmt und verarbeitet. Also müssen wir die Klassen möglichst abstrakt bauen und die Verarbeitung den Nachfahren überlassen.
 +
 
 +
 
 
'''C++'''
 
'''C++'''
<cpp>class TAdapter
+
<source lang="cpp">class TAdapter
 
{
 
{
 
   public:
 
   public:
Zeile 61: Zeile 78:
  
 
typedef TAdapter* (*CreateAdapterInstanceCallback)();//ruft den constructor auf und LoadFile wird gesondert aufgerufen
 
typedef TAdapter* (*CreateAdapterInstanceCallback)();//ruft den constructor auf und LoadFile wird gesondert aufgerufen
 +
</source>
 +
  
class TAdapterManager
 
{
 
  protected:
 
    map<string,CreateAdapterInstanceCallback> RegisteredAdapters;
 
  public:
 
    virtual void RegisterAdapter(string ExtensionName, CreateAdapterInstanceCallback Callback);
 
    virtual TAdapter* GetAdapterInstance(string ExtensionName);
 
    virtual bool RemoveAdapter(string ExtensionName);
 
};</cpp>
 
 
'''Pascal'''
 
'''Pascal'''
<pascal>TAdapter=class
+
<source lang="pascal">TAdapter = class
 
   public:
 
   public:
     function LoadFile(FileName:string):Boolean; virtual; abstract;
+
     function LoadFile(FileName: String): Boolean; virtual; abstract;
     function GetData():pointer; virtual; abstract;
+
     function GetData(): Pointer; virtual; abstract;
 
end;
 
end;
  
//Anmerkung: Objekte Pascal kennt die Möglickeit class of class und diese kann man anstatt eines Callbacks verwenden.
+
CreateAdapterInstanceCallback = function: TAdapter; //ruft nur den Konstruktor auf, geladen wird erst nach der Übergabe der Instanz</source>
//          Nutzen tut man dieses dann so. var AdapterCls:TAdapterCls; MyAdapter:TAdapter; begin MyAdapter:=AdapterCls.Create(); end;
+
 
//TAdapterCls=class of TAdpater;
+
== Beispiel ==
CreateAdapterInstanceCallback=function ():TAdapter;//ruft nur den Konstruktor auf, geladen wird erst nach der übergabe der Instanz
 
  
TAdapterManager=class
+
Nun mal ein kleines Beispiel für die Einsetzung des Pattern:
  protected:
+
 
    RegisteredAdapters:TUniqueHashList;//Eine Liste in der kein Wert doppelt vorkommt und die Strings durch Hashes im Zugriff optimiert werden.
+
 
  public:
+
Wir haben einen Adaptermanager für Konfigurationsdateien, ein Adapter für das Lesen von XML Dateien und eine Klasse, die die Konfiguration auswertet:
    //procedure RegisterAdapter(ExtensionName:string; AdapterCls:TAdapterCls);
 
    procedure RegisterAdapter(ExtensionName:string; Callback:CreateAdapterInstanceCallback);
 
    function GetAdapterInstance(ExtensionName:string):pTAdapter:
 
    function RemoveAdapter(ExtensionName:string):Boolean;
 
end;</pascal>
 
  
Nun mal ein kleines Beispiel für die einsetzung des Pattern.<br>
 
Wir haben einen AdapterManager für Konfigurationsdatein, ein Adapter für das lesen von XML Datein und eine Klasse, die die Konfiguration auswertet.<br>
 
 
'''C++'''
 
'''C++'''
<cpp>class TAdapter
+
<source lang="cpp">class TAdapter
 
{
 
{
 
   public:
 
   public:
Zeile 168: Zeile 170:
 
   cout<<Config->GetResolution()<<end;//0 oder Wert aus der XML File.
 
   cout<<Config->GetResolution()<<end;//0 oder Wert aus der XML File.
 
   Manager->RemoveAdapter(".xml");
 
   Manager->RemoveAdapter(".xml");
}</cpp>
+
}</source>
 +
 
 +
 
 
'''Pascal'''
 
'''Pascal'''
<pascal>TAdapter=class
+
<source lang="pascal">TAdapter = class
 
   public:
 
   public:
     function LoadFile(FileName:string):Boolean; virtual; abstract;
+
     function LoadFile(FileName: String): Boolean; virtual; abstract;
     function GetData():pointer; virtual; abstract;
+
     function GetData(): Pointer; virtual; abstract;
 
end;
 
end;
  
TConfigData=packed record
+
PConfigData = ^TConfigData;
   Resolution:cardinal;
+
TConfigData = packed record
 +
   Resolution: Cardinal;
 
end;
 
end;
  
TConfigAdapterForXML=class(TAdapter)
+
TConfigAdapterForXML = class(TAdapter)
 
   protected:
 
   protected:
     ConfigFile:^TConfigData;
+
     ConfigFile: PConfigData;
 
   public:
 
   public:
     function LoadFile(FileName:string):Boolean; overload;
+
     function LoadFile(FileName: String): Boolean; overload;
     function GetData():pointer; overload;     
+
     function GetData(): Pointer; overload;     
 
end;
 
end;
  
function TConfigAdapterForXML.LoadFile(FileName:string;
+
function TConfigAdapterForXML.LoadFile(FileName: String);
 
begin
 
begin
   ...lade und parse XML Datei...
+
   //...lade und parse XML Datei...
   getmem(ConfigFile,sizeof(TConfigData));
+
   GetMem(ConfigFile, SizeOf(TConfigData));
   ConfigFile.Resolution=XMLParsedResolutionValue;
+
   ConfigFile.Resolution = XMLParsedResolutionValue;
   ...Datei schliessen Parser freigeben...
+
   //...Datei schliessen Parser freigeben...
 
end;
 
end;
  
function TConfigAdapterForXML.GetData():pointer;
+
function TConfigAdapterForXML.GetData(): Pointer;
 
begin
 
begin
   result:=ConfigFile;
+
   Result := ConfigFile;
 
end;
 
end;
  
//Anmerkung: Objekte Pascal kennt die Möglickeit class of class und diese kann man anstatt eines Callbacks verwenden.
+
CreateAdapterInstanceCallback = function: TAdapter; //ruft nur den Konstruktor auf, geladen wird erst nach der Übergabe der Instanz
//          Nutzen tut man dieses dann so. var AdapterCls:TAdapterCls; MyAdapter:TAdapter; begin MyAdapter:=AdapterCls.Create(); end;
 
//TAdapterCls=class of TAdpater;
 
CreateAdapterInstanceCallback=function ():TAdapter;//ruft nur den Konstruktor auf, geladen wird erst nach der übergabe der Instanz
 
  
function MyConfigXMLAdapter():TAdapter;
+
function MyConfigXMLAdapter(): TAdapter;
 
begin
 
begin
   result:=TConfigAdapterForXML.Create();
+
   Result := TConfigAdapterForXML.Create();
 
end;
 
end;
  
TAdapterManager=class
+
TAdapterManager = class
 
   protected:
 
   protected:
     RegisteredAdapters:TUniqueHashList;//Eine Liste in der kein Wert doppelt vorkommt und die Strings durch Hashes im Zugriff optimiert werden.
+
     RegisteredAdapters: TUniqueHashList;
 
   public:
 
   public:
     //procedure RegisterAdapter(ExtensionName:string; AdapterCls:TAdapterCls);
+
     //procedure RegisterAdapter(ExtensionName: String; AdapterCls:TAdapterCls);
     procedure RegisterAdapter(ExtensionName:string; Callback:CreateAdapterInstanceCallback);
+
     procedure RegisterAdapter(ExtensionName: String; Callback: CreateAdapterInstanceCallback);
     function GetAdapterInstance(ExtensionName:string):pTAdapter:
+
     function GetAdapterInstance(ExtensionName: String): TAdapter:
     function RemoveAdapter(ExtensionName:string):Boolean;
+
     function RemoveAdapter(ExtensionName: String): Boolean;
 
end;
 
end;
  
TConfig=class
+
TConfig = class
 
   protected:
 
   protected:
     Resolution:cardinal;
+
     Resolution: Cardinal;
     Manager:TAdapterManager;
+
     Manager: TAdapterManager;
 
   public:
 
   public:
     constructor Create(AManager:TAdapterManager);
+
     constructor Create(AManager: TAdapterManager);
     function Load():Boolean;
+
     function Load(): Boolean;
     function GetResolution():cardinal;
+
     function GetResolution(): Cardinal;
 
end;
 
end;
  
constructor TConfig.Create(AManager:TAdapterManager);
+
constructor TConfig.Create(AManager: TAdapterManager);
 
begin
 
begin
   Manager:=AManager;
+
   Manager := AManager;
   Resolution:=0;
+
   Resolution := 0;
 
end;
 
end;
  
 
function TConfig.Load();
 
function TConfig.Load();
 
var
 
var
   Adapter:TAdapter;
+
   Adapter: TAdapter;
 
begin
 
begin
   Adapter:=Manager.GetAdapterInstance('.xml');
+
   Adapter := Manager.GetAdapterInstance('.xml');
   if assigned(Adapter) then
+
   if Assigned(Adapter) then
 
   begin
 
   begin
 
     if Adapter.LoadFile('playerconfig.xml') then
 
     if Adapter.LoadFile('playerconfig.xml') then
       Resolution:=TConfigData(Adapter.GetData()^).Resolution
+
       Resolution := TConfigData(Adapter.GetData()^).Resolution
 
     else
 
     else
       Resolution:=DEFAULT_RESOLUTION;
+
       Resolution := DEFAULT_RESOLUTION;
     Adapter.free();
+
     Adapter.Free();
 
   end;
 
   end;
 
end;
 
end;
  
function TConfig.GetResolution():cardinal;
+
function TConfig.GetResolution(): Cardinal;
 
begin
 
begin
   result:=Resolution;
+
   Result := Resolution;
 
end;
 
end;
  
 
var
 
var
   Manager:TAdapterManager;
+
   Manager: TAdapterManager;
   Config:TConfig;
+
   Config: TConfig;
 
begin
 
begin
   Manager:=TAdapterManager.Create();
+
   Manager := TAdapterManager.Create();
   Manager.RegisterAdapter('.xml',MyConfigXMLAdapter);
+
   Manager.RegisterAdapter('.xml', MyConfigXMLAdapter);
   Config:=TConfig.Create(Manager);
+
   Config := TConfig.Create(Manager);
   writeln(Config.GetResolution());//0
+
   Writeln(Config.GetResolution()); //0
 
   Config.Load();
 
   Config.Load();
   writeln(Config.GetResolution());//0 oder Wert aus der XML File.
+
   Writeln(Config.GetResolution()); //0 oder Wert aus der XML File.
 
   Manager.RemoveAdapter('.xml');
 
   Manager.RemoveAdapter('.xml');
}</pascal>
+
}</source>
Dies war ein genauerer Pseudocode, beide Versionen sind nicht getestet aber man versteht den sinn doch denke ich sehr schnell.<br>
+
 
Man könnte nun weitere Adapter für ini,txt,windows registry und so weiter schreiben, der Config Klasse würde dies nichts ausmachen, da es ja seine gewollten Daten bekommt und verarbeiten kann.<br>
+
= Fazit =
 +
 
 +
Dies war ein genauerer Pseudocode, beide Versionen sind nicht getestet, aber man sollte den Sinn recht schnell verstehen können. Man könnte nun weitere Adapter für ''ini'', ''txt'', ''windows registry'' und so weiter schreiben, der Config-Klasse würde dies nichts ausmachen, da es ja seine gewollten Daten bekommt und verarbeiten kann.
  
 
Einsatzbeispiele wären Texturen, Sound, Models, Materials, Scripte und Shader.
 
Einsatzbeispiele wären Texturen, Sound, Models, Materials, Scripte und Shader.
Mein TexturManager z.B. nutzt Adapter für DDS und der TexturAdapter ist so konzipiert, dass man sogar Bildsequenz basierte Formate(ala avi) davon ableiten könnte.<br>
+
Mein TexturManager z.B. nutzt Adapter für DDS und der TexturAdapter ist so konzipiert, dass man sogar Bildsequenz basierte Formate (àla avi) davon ableiten könnte.
Dafür muss man im Adapter und Manager eine Updatemethode einbauen, im Manager eine Liste für Updatebare Adapter einführen und im Adapter ein Flag für Updatebar hinzufügen.<br>
+
Da man Adapter registrieren kann und sie nicht fest verdrahtet sind, kann man diese z.B. in Bibliotheken packen und so ein Pluginsystem realisieren. Ich selber rate allerdings von solch einem Pluginsystem ab, da es die Engine sehr unsicher macht und die Plugins Fehler mit in die Software bringen können.
Die TexturManager.Update Methode wird dann jeden Frame aufgerufen und diese ruft alle in der UpdatebarenAdapterListe auf.<br>
+
 
Update, im Adapter, kann dann sich die Zeit holen und entscheiden, ob es die Bilddaten updated, wenn ja dann werden die verwendeten Bibliotheken(z.B. VFW,libmpeg,...) aufgerufen und das nächste Frame geholt.<br>
+
Bei Fragen, Kritik, Verbesserungsvorschläge oder ähnlichem einfach mir ([[Benutzer:TAK2004|TAK2004]]) eine PM im Forum schreiben.
Da man die Adapter registrieren kann und sie nicht fest verdrahtet sind, kann man diese z.B. in shared libraries(auch als .so und .dll bekannt) packen und so ein Pluginsystem realisieren.<br>
 
Ich selber rate allerdings von solch ein Pluginsystem ab, da es die Engine sehr unsicher macht und die Plugins Fehler mit in die Software bringen kann.<br>
 
<br>
 
Bei Fragen, Kritik, Verbesserungsvorschläge oder ähnlichem einfach mir(TAK2004) eine PM im Forum schreiben.
 

Aktuelle Version vom 10. März 2009, 19:42 Uhr

Konzept

Das Adapter Muster ist, wie der Name schon sagt, ein Muster, welches als ein Adapter zwischen 2 Objekten fungiert. Diese Muster ist sehr nützlich bei dem flexiblen Programmieren von ressourcenabhängigen Managern.

Das Konzept des Adapters ist es, Daten, die in einen Format vorliegen, durch den Adapter in ein für die andere nutzbare Form zu bringen. So kann ein Adapter z.B. Bitmaps lesen und diese in das benötigte Format umwandeln, ein anderer Adapter kann dies für ein PNG und ein weiterer kann dies z.B. für eine AVI.

Umsetzung

Skizze

Wir haben gerade von mehren möglichen Adaptern geredet, die verschiedene Bildformate und Bildsequenzformate lesen sollen. Also sollten wir erstmal ermöglichen, dass man mehrere Adapter mit jeweils einen Dateiformat verbinden kann.


C++

class TAdapter
{
  public:
    TAdapter(string FileName);
};

typedef TAdapter* (*CreateAdapterInstanceCallback)(string FileName);

class TAdapterManager
{
  protected:
    map<string,CreateAdapterInstanceCallback> RegisteredAdapters;
  public:
    void RegisterAdapter(string ExtensionName, CreateAdapterInstanceCallback Callback);
    TAdapter* GetAdapterInstance(string ExtensionName);
    bool RemoveAdapter(string ExtensionName);
};


Pascal

TAdapter = class
  public:
    constructor Create(FileName: String);
end;

(*
Anmerkung: Objekt Pascal kennt die Möglichkeit class of class und diese kann man anstatt eines Callbacks verwenden.
Nutzen tut man dieses dann so:
type
  TAdapterCls = class of TAdapter;
var 
  AdapterCls: TAdapterCls;
  MyAdapter: TAdapter;
begin
  MyAdapter := AdapterCls.Create();
end;
*)

CreateAdapterInstanceCallback = function (FileName: String): TAdapter;

TAdapterManager = class
  protected:
    RegisteredAdapters: TUniqueHashList; //Eine Liste in der kein Wert doppelt vorkommt und die Strings durch Hashes im Zugriff optimiert werden.
  public:
    procedure RegisterAdapter(ExtensionName: String; Callback: CreateAdapterInstanceCallback);
    function GetAdapterInstance(ExtensionName: String): TAdapter:
    function RemoveAdapter(ExtensionName: String): Boolean;
end;

Jetzt können wir einen eigenen Adapter dem Adaptermanager bekannt machen, indem wir die Extension(z.B. "gz" oder "avi") und ein passenden Callback übergeben. Der Callback besitzt nur eine Zeile Code, denn er erzeugt nur eine Instanz und gibt diese zurück.

Abstrahierung

Nun können wir Adapter registrieren, erstellen und entfernen, aber was ist mit dem Austausch von Daten?

Da der Adapter wissen muss, was der Empfänger für Daten haben will, muss der Adapter für den Empfänger zugeschnitten werden und der Empfänger braucht auch eine Routine, die die Daten vom Adapter entgegen nimmt und verarbeitet. Also müssen wir die Klassen möglichst abstrakt bauen und die Verarbeitung den Nachfahren überlassen.


C++

class TAdapter
{
  public:
    virtual bool LoadFile(string FileName)=0;
    virtual void* GetData()=0;
};

typedef TAdapter* (*CreateAdapterInstanceCallback)();//ruft den constructor auf und LoadFile wird gesondert aufgerufen


Pascal

TAdapter = class
  public:
    function LoadFile(FileName: String): Boolean; virtual; abstract;
    function GetData(): Pointer; virtual; abstract;
end;

CreateAdapterInstanceCallback = function: TAdapter; //ruft nur den Konstruktor auf, geladen wird erst nach der Übergabe der Instanz

Beispiel

Nun mal ein kleines Beispiel für die Einsetzung des Pattern:


Wir haben einen Adaptermanager für Konfigurationsdateien, ein Adapter für das Lesen von XML Dateien und eine Klasse, die die Konfiguration auswertet:

C++

class TAdapter
{
  public:
    virtual bool LoadFile(string FileName)=0;
    virtual void* GetData()=0;
};

struct TConfigData
{
  unsigned int Resolution;
};

class TConfigAdapterForXML:public TAdapter
{
  protected:
    TConfigData* ConfigFile;
  public:
    bool LoadFile(string FileName){
      ...lade und parse XML Datei...
      ConfigFile=new TConfigData;
      ConfigFile->Resolution=XMLParsedResolutionValue;
      ...Datei schliessen Parser freigeben...
    }
    void* GetData(){
      return (void*)ConfigFile;
    }
}

typedef TAdapter* (*CreateAdapterInstanceCallback)();

TAdapter* MyConfigXMLAdapter(){
  return new TConfigAdapterForXML;
}

class TAdapterManager
{
  protected:
    map<string,CreateAdapterInstanceCallback> RegisteredAdapters;
  public:
    virtual void RegisterAdapter(string ExtensionName, CreateAdapterInstanceCallback Callback);
    virtual TAdapter* GetAdapterInstance(string ExtensionName);
    virtual bool RemoveAdapter(string ExtensionName);
};

class TConfig{
  protected:
    unsigned int Resolution;
    TAdapterManager* Manager;
  public:
    TConfig(TAdapterManager* AManager){ Manager=AManager; Resolution=0; }
    bool Load(){
      TAdapter* Adapter=Manager->GetAdapterInstance(".xml");
      if (Adapter)
      {
        if(Adapter->LoadFile("playerconfig.xml"))
          Resolution=(TConfigData*)(Adapter->GetData())->Resolution;
        else
          Resolution=DEFAULT_RESOLUTION;
        delete Adapter;
      }
    }
    unsigned int GetResolution(){ return Resolution; }
};

int main(){
  TAdapterManager* Manager=new TAdapterManager;
  Manager->RegisterAdapter(".xml",MyConfigXMLAdapter);
  TConfig* Config=new TConfig(Manager);
  cout<<Config->GetResolution()<<end;//0
  Config.Load();
  cout<<Config->GetResolution()<<end;//0 oder Wert aus der XML File.
  Manager->RemoveAdapter(".xml");
}


Pascal

TAdapter = class
  public:
    function LoadFile(FileName: String): Boolean; virtual; abstract;
    function GetData(): Pointer; virtual; abstract;
end;

PConfigData = ^TConfigData;
TConfigData = packed record
  Resolution: Cardinal;
end;

TConfigAdapterForXML = class(TAdapter)
  protected:
    ConfigFile: PConfigData;
  public:
    function LoadFile(FileName: String): Boolean; overload;
    function GetData(): Pointer; overload;    
end;

function TConfigAdapterForXML.LoadFile(FileName: String);
begin
  //...lade und parse XML Datei...
  GetMem(ConfigFile, SizeOf(TConfigData));
  ConfigFile.Resolution = XMLParsedResolutionValue;
  //...Datei schliessen Parser freigeben...
end;

function TConfigAdapterForXML.GetData(): Pointer;
begin
  Result := ConfigFile;
end;

CreateAdapterInstanceCallback = function: TAdapter; //ruft nur den Konstruktor auf, geladen wird erst nach der Übergabe der Instanz

function MyConfigXMLAdapter(): TAdapter;
begin
  Result := TConfigAdapterForXML.Create();
end;

TAdapterManager = class
  protected:
    RegisteredAdapters: TUniqueHashList;
  public:
    //procedure RegisterAdapter(ExtensionName: String; AdapterCls:TAdapterCls);
    procedure RegisterAdapter(ExtensionName: String; Callback: CreateAdapterInstanceCallback);
    function GetAdapterInstance(ExtensionName: String): TAdapter:
    function RemoveAdapter(ExtensionName: String): Boolean;
end;

TConfig = class
  protected:
    Resolution: Cardinal;
    Manager: TAdapterManager;
  public:
    constructor Create(AManager: TAdapterManager);
    function Load(): Boolean;
    function GetResolution(): Cardinal;
end;

constructor TConfig.Create(AManager: TAdapterManager);
begin
  Manager := AManager;
  Resolution := 0;
end;

function TConfig.Load();
var
  Adapter: TAdapter;
begin
  Adapter := Manager.GetAdapterInstance('.xml');
  if Assigned(Adapter) then
  begin
    if Adapter.LoadFile('playerconfig.xml') then
      Resolution := TConfigData(Adapter.GetData()^).Resolution
    else
      Resolution := DEFAULT_RESOLUTION;
    Adapter.Free();
  end;
end;

function TConfig.GetResolution(): Cardinal;
begin
  Result := Resolution;
end;

var
  Manager: TAdapterManager;
  Config: TConfig;
begin
  Manager := TAdapterManager.Create();
  Manager.RegisterAdapter('.xml', MyConfigXMLAdapter);
  Config := TConfig.Create(Manager);
  Writeln(Config.GetResolution()); //0
  Config.Load();
  Writeln(Config.GetResolution()); //0 oder Wert aus der XML File.
  Manager.RemoveAdapter('.xml');
}

Fazit

Dies war ein genauerer Pseudocode, beide Versionen sind nicht getestet, aber man sollte den Sinn recht schnell verstehen können. Man könnte nun weitere Adapter für ini, txt, windows registry und so weiter schreiben, der Config-Klasse würde dies nichts ausmachen, da es ja seine gewollten Daten bekommt und verarbeiten kann.

Einsatzbeispiele wären Texturen, Sound, Models, Materials, Scripte und Shader. Mein TexturManager z.B. nutzt Adapter für DDS und der TexturAdapter ist so konzipiert, dass man sogar Bildsequenz basierte Formate (àla avi) davon ableiten könnte. Da man Adapter registrieren kann und sie nicht fest verdrahtet sind, kann man diese z.B. in Bibliotheken packen und so ein Pluginsystem realisieren. Ich selber rate allerdings von solch einem Pluginsystem ab, da es die Engine sehr unsicher macht und die Plugins Fehler mit in die Software bringen können.

Bei Fragen, Kritik, Verbesserungsvorschläge oder ähnlichem einfach mir (TAK2004) eine PM im Forum schreiben.