Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Aufbau logisch und korrekt? (https://www.delphipraxis.net/146218-aufbau-logisch-und-korrekt.html)

ChEeTaH 15. Jan 2010 21:31


Aufbau logisch und korrekt?
 
Hallo
Ich habe vor eine Parser-Klasse zu schreiben, die verschiedene Dienste im Internet Parst und formatiert ausgibt. Meine Klasse soll vorerst Google, Milw0rm, Heise, Gulli, Tecchannel und Wikipedia unterstützen. Ich lese dabei den Titel und wenn verfügbar den Link der jeweiligen Seite aus.

Bsp. Google:
Benutzer gibt Query 'delphi forum' ein.
Klasse gibt als Titel (in dem Fall der erste Googleeintrag) 'Delphi-PRAXIS - Hier werden sie geholfen' aus und als Link 'http://delphipraxis.net'.

Die Klasse soll später in einen IRC Bot Integriert werden, also arbeitet sie Zeilenbasiert.
(Das nur nebenbei, ist eher unwichtig. Mein Ziel ist es erstmal die Klasse zu schreiben).

Nun möchte ich von euch Wissen, ob mein Aufbau logisch und strukturiert erscheint oder ob ich etwas verbessern kann. Das Programm ist noch nicht fertig, es ist lediglich der Ansatz da und möchte Wissen ob der Ansatz korrekt ist.

Allgemeiner Aufbau:
Rahmen-Klasse: TParseServices (wird aufgerufen)
Delphi-Quellcode:
...
PS.Service := svGoogle;
PS.Query := 'delphi forum';
PS.Submit; {Submit liest den Service aus und übergibt dann den Query an die jeweilige UnterKlasse}

Titel := PS.GetResult.Title.Strings[0]; // Gibt 'Delphi-PRAXIS - Hier werden sie geholfen'
Link := PS.GetResult.Link.Strings[0]; // Gibt 'http://delphipraxis.net'

uParseServices:
Delphi-Quellcode:
unit uParseServices;

interface
uses IdBaseComponent, IdComponent, IdTCPConnection,
  IdTCPClient, IdHTTP, StrUtils, Classes;

type
  TServices = (svGoogle, svMilw0rm, svHeise, svGulli, svTecchannel,
    svWikipedia);

type
  TResult = record
    Title: TStrings;
    Link: TStrings;
  end;

type
  TPattern = record
    FBegin: string;
    FEnd: string;
  end;

type
  TGoogle = class
  private
    FPatternTitle: TPattern;
    FPatternLink: TPattern;
    FQueryURL: string;
    function Parse: TResult;
  end;

type
  TMilw0rm = class
  private
    FPatternTitle: TPattern;
    FQueryURL: string;
    function Parse: TResult;
  end;

type
  THeise = class
  private
    FQueryURL: string;
    function Parse: TResult;
  end;

type
  TGulli = class
  private
    FQueryURL: string;
    function Parse: TResult;
  end;

type
  TTecChannel = class
  private
    FQueryURL: string;
    function Parse: TResult;
  end;

type
  TWikipedia = class
  private
    FPatternTitle: TPattern;
    FPatternArticle: TPattern;
    FQueryURL: string;
    function Parse: string; // Erster Artikel
  end;

{(*}const
      GoogleURL    = 'http://google.de/?q=[<QUERY>]';
      Milw0rmURL   = 'http://milw0rm.com/?q=[<QUERY>]';
      HeiseURL     = 'http://heise.de/?q=[<QUERY>]';
      GulliURL     = 'http://gulli.com/?q=[<QUERY>]';
      TecChannelURL = '...[<QUERY>]';
      WikipediaURL = 'http://wikipedia.de/go?q=[<QUERY>]&l=de&e=wikipedia&s=suchen&b=suchen'; {*)}

type
  TParseServices = class

  private
    FResponse: string;
    FQuery: string;
    FService: TServices;
    FFilterdLinks: TStrings;
    FFilterdTitles: TStrings;

    Google: TGoogle;
    Milw0rm: TMilw0rm;
    Heise: THeise;
    Gulli: TGulli;
    TecChannel: TTecChannel;
    Wikipedia: TWikipedia;
  public
    property Service: TServices read FService write FService;
    property Query: string read FQuery write FQuery;
    procedure Submit;

    function Results: TResult;
    property GetResult: TResult read Results;
      // Gibt den Titel und die Links wieder

    property GetRawHTTPResponse: string read FResponse;
    // Gibt den kompletten HTTP Response zurück
    constructor Create;
    destructor Destroy; override;
  end;

implementation

constructor TParseServices.Create;
begin
  inherited;
  Google := TGoogle.Create;
  Milw0rm := TMilw0rm.Create;
  Heise := THeise.Create;
  Gulli := TGulli.Create;
  TecChannel := TTecChannel.Create;
  Wikipedia := TWikipedia.Create;

  FFilterdLinks.Create;
  FFilterdTitles.Create;

  // [<QUERY>] als Platzhalter wird mit StringReplace ersetzt
  // Stimmen nicht alle, nur für Anschauung präsent
  Google.FQueryURL := GoogleURL;
  Milw0rm.FQueryURL := Milw0rmURL;
  Heise.FQueryURL := HeiseURL;
  Gulli.FQueryURL := GulliURL;
  TecChannel.FQueryURL := TecChannelURL;
  Wikipedia.FQueryURL := WikipediaURL;

  // Parst den Content
  with Google do
  begin
    FPatternTitle.FBegin := ''; // Pattern kommen noch :)
    FPatternTitle.FEnd := '';
    FPatternLink.FBegin := '';
    FPatternLink.FEnd := '';
  end;

  with Milw0rm do
  begin
    FPatternTitle.FBegin := '';
    FPatternTitle.FEnd := '';
  end;

  with Heise do
  begin

  end;

  with Gulli do
  begin

  end;

  with TecChannel do
  begin

  end;

  with Wikipedia do
  begin
    FPatternTitle.FBegin := '<h1 id="firstHeading" class="firstHeading">';
    // Titel      <h1 id="firstHeading" class="firstHeading">Titel</h1>
    FPatternTitle.FEnd := '</h1>';
    FPatternArticle.FBegin := '

';
    // Content    

...</p><table id="toc" class="toc">
    FPatternArticle.FEnd := '</p><table id="toc" class="toc">';
  end;
end;

destructor TParseServices.Destroy;
begin
  Google.Free;
  Milw0rm.Free;
  Heise.Free;
  Gulli.Free;
  TecChannel.Free;
  Wikipedia.Free;

  FFilterdLinks.Free;
  FFilterdTitles.Free;
  inherited;
end;

procedure TParseServices.Submit;
begin
  case Service of
    svGoogle:
      begin
        Google.Parse;
      end;
    svMilw0rm:
      begin

      end;
    svHeise:
      begin

      end;
    svGulli:
      begin

      end;
    svTecchannel:
      begin

      end;
    svWikipedia:
      begin

      end;
  end;
end;

function TGoogle.Parse: TResult;
var
  Titles: TStrings;
  Links: TStrings;
begin
  Titles := TStringList.Create;
  Links := TStringList.Create;
  // ToDo: FQueryURL wird aufgerufen und die Anwort in FResponse gespeichert
  // Dann wird mitPos, PosEx, Copy geparst bis Titel und Links gefiltert sind

  Titles.Add('ATitle');
  Links.Add('ALink');
  Parse.Title := Titles;
  Parse.Link := Links;

  Links.Free;
  Titles.Free;
end;

function TParseServices.Results: TResult;
begin
  Results.Link := FFilterdLinks;
  Results.Title := FFilterdTitles;
end;

end.

Namenloser 15. Jan 2010 21:47

Re: Aufbau logisch und korrekt?
 
Ich würde es vermeiden, die Unterscheidung über case-Strukturen zu regeln. Das führt dazu, dass du an zig Stellen etwas verändern musst, sobald du eine weitere Seite hinzufügen willst, und führt zu unübersichtlichem Code. Leite stattdessen alle spezialisierten Seiten-Klassen von einer Basisklasse ab und biete die Methoden über ein einheitliches Interface an.

Das Strategie-Pattern ist wahrscheinlich, was du suchst.

ChEeTaH 15. Jan 2010 21:55

Re: Aufbau logisch und korrekt?
 
Danke für die Antwort.
Könntest du mir dazu ein Delphi Beispiel geben? Ich bin leider (noch) kein ausgebildeter Informatiker :)
Edit: Wäre das hier eine Implementation?

Namenloser 15. Jan 2010 23:11

Re: Aufbau logisch und korrekt?
 
Zitat:

Zitat von ChEeTaH
Ich bin leider (noch) kein ausgebildeter Informatiker :)

Ich auch nicht :wink:

Aber ich habe mir jetzt deinen Quellcode noch einmal genauer angesehen, und dabei ist mir aufgefallen: Wozu brauchst du überhaupt TParseServices?

Eine sinnvolle, logische Klassenstruktur sähe für mich so aus:
Delphi-Quellcode:
uses
  contnrs; // für TObjectList

type

  TSearchResult = class
  private
    FTitle, FLink: string;
  public
    property Title: string read FTitle write FTitle;
    property Link: string read FLink write FLink;
  end;

  TSearchResultList = class(TObjectList)
  private
    function GetItem(Index: integer): TSearchResult;
    procedure SetItem(Index: integer; Item: TSearchResult);
  public
    property Items[index: integer]: TSearchResult read GetItem write SetItem; default;
    function Add(Item: TSearchResult): integer; override;
    { ... }
  end;

  TSearchProvider = class
  private
    FResults: TSearchResults;
    FQuery: string;
  protected
    function BuildRequestURL(Query: string): string; virtual; abstract;
    procedure Parse(Content: string); virtual; abstract;
  public
    property Results: TSearchResultList read FResults;
    property Query: string read FQuery write FQuery;
    procedure Submit;

    constructor Create;
    destructor Destroy; override;
  end;

implementation

{ TSearchProvider }

constructor TSearchProvider.Create;
begin
  inherited;
  FResults := TSearchResultList.Create(True);
end;

constructor TSearchProvider.Destroy;
begin
  FResults.Free;
end;

procedure TSearchProvider.Submit;
var
  Content: string;
begin
  Content := HTTP_GET(BuildRequestURL(FQuery)); // ausgedachte Funktion... konkrete umsetzung überlasse ich dir

  Results.Clear;
  Parse(Content);
end;

{ TSearchResultList }

function TSearchResultList.GetItem(index: integer): TSearchResult;
begin
  Result := TSearchResult(inherited GetItem(index));
end;

procedure TSearchResultList.SetItem(index: integer; Item: TSearchResult);
begin
  inherited SetItem(index, Item);
end;

function TSearchResultList.Add(Item: TSearchResult);
begin
  Result := inherited Add(Item);
end;
Eine konkrete Implementierung eines bestimmten Searchproviders könnte dann so aussehen:
Delphi-Quellcode:
type
  TGoogle = class(TSearchProvider)
  protected
    function BuildRequestURL(Query: string): string; override;
    procedure Parse(Content: string); override;
  end;

implementation

function TGoogle.BuildRequestURL(Query: string): string;
begin
  Result := Format('http://google.de/?q=%s', [Query]);
end;

procedure TGoogle.Parse(Content: string); override;
var
  SearchResult: TSearchResult;
begin
  // Suchergebnisse in Quellcode suchen...

  SearchResult := TSearchResult.Create;
  SearchResult.Title := '...';
  SearchResult.Link := '...';
  Results.Add(Searchresult);

  // ...
end;
Und ein Aufruf dann so:
Delphi-Quellcode:
var
  SearchProvider: TSearchProvider;
  i: integer;
begin
  SearchProvider := TGoogle.Create;
//  SearchProvider := THeise.Create;
//  SearchProvider := TGulli.Create;
  SearchProvider.Query := 'delphi forum';
  SearchProvider.Submit;
  for i := 0 to SearchProvider.Results.Count -1 do
  begin
    WriteLn(Format('Titel: %s;   Link: %s',
      [SearchProvider.Results[i].Title,
       SearchProvider.Results[i].Link]));
  end;
  SearchProvider.Free;
end;
Nach dem Schema von TGoogle kannst du dir auch für alle anderen Seiten entsprechende Suchprovider erstellen.

Bitte beachte: Dieser Code wurde im Beitragseditor getippt und enthält sicher ein paar Fehlerchen - macht aber auch nix, schließlich geht es eher um die grobe Struktur.

[edit]
Zitat:

Zitat von ChEeTaH
Edit: Wäre das hier eine Implementation?

Da es in großen Lettern in der Überschrift steht, wäre das anzunehmen, ja :wink:
[/edit]


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:14 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz