Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   class helper generic class (https://www.delphipraxis.net/183047-class-helper-generic-class.html)

bernhard_LA 7. Dez 2014 20:49

class helper generic class
 
unter http://stackoverflow.com/questions/1...-generic-class wird als Einschränkung "keine class helper " für generische Datentypen genannt.

Dh. ich kann keine Unit aufbauen

Delphi-Quellcode:
unit GraphGenericHelperTypes;

interface

uses MygenericGraphTypes;


type TVertexhelper<T>= class helper for TVertex<T>
   
     OutputAttributes: TVertexOutputAttributes;
   
     function HasAdditionalAttributes: Boolean
end;

implementation


end.

um einen Datentyp TVertex<T> wie in http://sourceforge.net/projects/gengraph/files/ abzuwandeln.


ich möchte alle Funktionalität der Klassen TVertex<T> = class, TEdge<T, V> = class, ....
in einen Bestandteil der nur für die Graph-Mathematik nötig ist und einen Bestandteil der für die Visualisierung
via GraphViz (http://www.graphviz.org/) nötig ist auftrennen. Wenn immer sich das Tool zur Graph-Visualisierung ändert soll der Core Bestandteil der Graph-Klasse unverändert bestehen bleiben.

Gibt es eine alternative Lösung für dieses Problem in Delphi ?

Sir Rufo 7. Dez 2014 20:58

AW: class helper generic class
 
Du hast also sowas wie ein
Delphi-Quellcode:
TCoreVertex<T> = class
  procedure Foo;
end;
und so ein Visualisierungsgedöns
Delphi-Quellcode:
TVertex<T> = class( TCoreVertex<T> )
  procedure Bar;
end;
?
Oder wie muss ich das verstehen?

himitsu 7. Dez 2014 22:25

AW: class helper generic class
 
Es gibt halt keine generischen Class-Helper.
Genauso, wie man keine generischen Prozeduren deklarieren kann. :cry:

Man kann maximal einen ungenerischen Class-Helper für einen Generic definieren.

bernhard_LA 7. Dez 2014 23:01

AW: class helper generic class
 
der Plan geht wie folgt :

eine Klasse für TVertex nur mit Eigenschaften die eine Vertex auch hat....
Delphi-Quellcode:
  TVertex<T> = class
    public
    /// alles was hier kommt brauche ich für die Graph Mathematik
    ...
    ...
    end;
und dann gibt es noch


Delphi-Quellcode:
  TVertexhelper<T> = class helper for TVertex<T>
    public
    /// alles was hier kommt brauche ich ***  nur **** für die Visualisierung
    ...
    ...
    end;


alle Rechenoperationen laufen auf den Basis Klassen TVertex<T>; Wer was zeichnen lassen wir füllt bei Bedarf die Classhelper felder auf.

Vererben will ich nicht weil dann alle Algorithmen auch eine Ableitung von TVertex<T> erwarten und dies ist eigentlich unlogisch.

Sir Rufo 8. Dez 2014 06:51

AW: class helper generic class
 
Du musst ja auch nicht vererben, sondern du kannst ja auch wrappen.

Diese verlinkte Lib habe ich mir mal angesehen und mal kurz was zusammengefasst
Delphi-Quellcode:
unit GenericGraph.Global;

interface

uses
  SysUtils,
  Generics.Collections,
  GenericGraph.ColorSchemes,
  Variants;

type
{$REGION 'Documentation'}
  /// <summary>
  /// Definition for a marker, used for edges, vertices and graphs
  /// </summary>
{$ENDREGION}
  TMarker = Variant;

{$REGION 'Documentation'}
  /// <summary>
  /// An associative List of Markers
  /// </summary>
{$ENDREGION}
  TMarkerList = TDictionary<string, TMarker>;

  TCore = class
    Marker: TMarkerList;
{$REGION 'Documentation'}
    /// <summary>
    /// The degree is used in an undirected graph and determined the amount
    /// of direct neighbours to a vertex
    /// </summary>
{$ENDREGION}
    Degree: Integer;
    function HasAdditionalAttributes: Boolean; virtual;
    destructor Destroy; override;
    procedure Mark( Key: String; Value: TMarker );
    function GetMark( Key: String ): TMarker;
    function HasMark( Key: String ): Boolean;
    procedure UnMark( Key: String );
    function HasAnyMark( ): Boolean;
    procedure ClearMarks( );
  end;

  TCore<T> = class( TCore )
    Data: T;
  end;

{$REGION 'Documentation'}
  /// <summary>
  /// Contains attributes to customize the output of a TVertex
  /// </summary>
{$ENDREGION}

  TVertexOutputAttributes = record
    Caption: String;
    Shape: String;
    Color: TGraphVizColor;
    FontColor: TGraphVizColor;
    FillColor: String;
    FontSize: Integer;
    FontName: String;
    ReverseDirection: Boolean;
  end;

{$REGION 'Documentation'}
  /// <summary>
  /// Represents a single vertex in the graph
  /// </summary>
{$ENDREGION}

  TVertex<T> = class( TCore<T> )
  public
    Name: String;
    OutputAttributes: TVertexOutputAttributes;

{$REGION 'Documentation'}
    /// <summary>
    /// The in-degree is used in an directed graph and determines the amount
    /// of edges entering a vertex
    /// </summary>
{$ENDREGION}
    DegreeIn: Integer;

{$REGION 'Documentation'}
    /// <summary>
    /// The out-degree is used in an directed graph and determines the amount
    /// of edges&#65533;leaving a vertex
    /// </summary>
{$ENDREGION}
    DegreeOut: Integer;
    function HasAdditionalAttributes: Boolean; override;
  end;

{$REGION 'Documentation'}
  /// <summary>
  /// A simple ordered list of vertices
  /// </summary>
{$ENDREGION}

  TVertexList<T> = class( TList < TVertex < T >> );

{$REGION 'Documentation'}
  /// <summary>
  /// Contains attributes to customize the output of a TEdge
  /// </summary>
{$ENDREGION}

  TEdgeOutputOptions = record
    Caption: String;
    Color: String;
    Style: String;
  end;

{$REGION 'Documentation'}
  /// <summary>
  /// Represents a single edge in the graph
  /// First attribute is the data assigned to the edge
  /// Second attribute is the data assigned to the vertex (should match your implementation of TVertex<V>)
  /// </summary>
{$ENDREGION}

  TEdge<T, V> = class( TCore<T> )
    VertexA: TVertex<V>;
    VertexB: TVertex<V>;
    Directed: Boolean;
    Degree_Reverse: Integer;
    Weight: Integer;
    OutputAttributes: TEdgeOutputOptions;

    constructor Create( pDirected: Boolean );

    function HasAdditionalAttributes( ): Boolean; override;
  end;

  TEdgeList<T, V> = class( TList < TEdge < T, V >> );

  TGraphOutputOptions = record
    NodeSep: Double;
    RenderAsNonDirectedGraph: Boolean;

    // Added by mc.botha
    TrueColor, Splines, Overlap: Boolean;
    BGColor: TGraphVizColor;
  end;

  EBasicGraphException = Exception;
  EDuplicateEdge = class( EBasicGraphException );

implementation

{ TCore }

procedure TCore.ClearMarks;
begin
  FreeAndNil( Marker );
end;

destructor TCore.Destroy;
begin
  FreeAndNil( Marker );
  inherited;
end;

function TCore.GetMark( Key: String ): TMarker;
begin
  Result := Null;
  if Assigned( Marker )
  then
    begin
      if Marker.ContainsKey( Key )
      then
        Result := Marker.Items[Key];
    end;
end;

function TCore.HasAdditionalAttributes: Boolean;
begin
  Result := False;
end;

function TCore.HasAnyMark: Boolean;
begin
  Result := False;
  if Assigned( Marker )
  then
    Result := Marker.Count > 0;
end;

function TCore.HasMark( Key: String ): Boolean;
begin
  Result := False;
  if Assigned( Marker )
  then
    Result := Marker.ContainsKey( Key );
end;

procedure TCore.Mark( Key: String; Value: TMarker );
begin
  if not Assigned( Marker )
  then
    Marker := TMarkerList.Create;

  if Marker.ContainsKey( Key )
  then
    Marker.Items[Key] := Value
  else
    Marker.Add( Key, Value );
end;

procedure TCore.UnMark( Key: String );
begin
  Assert( Assigned( Marker ) );

  if Marker.ContainsKey( Key )
  then
    Marker.Remove( Key );

  if Marker.Count = 0
  then
    FreeAndNil( Marker );
end;

{ TVertex<T> }

function TVertex<T>.HasAdditionalAttributes: Boolean;
begin
  Result := ( Trim( OutputAttributes.Shape ) <> '' ) or ( Trim( OutputAttributes.Caption ) <> '' ) or ( Trim( OutputAttributes.Color ) <> '' ) or
    ( Trim( OutputAttributes.FontColor ) <> '' ) or ( OutputAttributes.FontSize <> 0 ) or ( OutputAttributes.ReverseDirection ) or
    ( Trim( OutputAttributes.FontName ) <> '' );
end;

{ TEdge<T, V> }

constructor TEdge<T, V>.Create( pDirected: Boolean );
begin
  inherited Create;
  Directed := pDirected;
  Weight := 1;
end;

function TEdge<T, V>.HasAdditionalAttributes: Boolean;
begin
  Result := ( Trim( OutputAttributes.Caption ) <> '' ) or ( Trim( OutputAttributes.Color ) <> '' ) or ( Trim( OutputAttributes.Style ) <> '' );
end;

end.
Wenn du jetzt eine andere Funktionalität brauchst, dann baue dir einen Wrapper, der arbeitet dann auch nicht anders als ein class helper.
Delphi-Quellcode:
TAbstractWrapper = class
end;

TCoreWrapper = class( TAbstractWrapper )
  constructor Create( AInstance : TCore );
end;

TCoreWrapper<T> = class( TCoreWrapper )
  constructor Create( AInstance : TCore<T>;
end;

TVertexWrapper<T> = class( TCoreWrapper<T> )
  constructor Create( AInstance : TVertex<T> );
end;

TEdgeWrapper<T,V> = class( TCoreWrapper<T> )
  constructor Create( AInstance : TEdge<T,V> );
end;
So kannst deine Funktionalitäten ziemlich DRY implementieren.

Dejan Vu 8. Dez 2014 07:08

AW: class helper generic class
 
Zitat:

Zitat von bernhard_LA (Beitrag 1282568)
alle Rechenoperationen laufen auf den Basis Klassen TVertex<T>; Wer was zeichnen lassen wir füllt bei Bedarf die Classhelper felder auf.

Hmm.. Im Endeffekt sind dann alle Operationen auf TVertex<T> definiert, was die Klasse ziemlich überladen erscheint. Du klatschtst zwar Funktionalität über Class Helper ran, aber im Endeffekt schmeißt Du doch alles in einen (TVertex<T>-)Topf.

Ich würde doch einfach separate Berechnungsklassen schreiben. Damit bist Du im Endeffekt doch besser dran (finde ich). So eine Klasse kann ja auch statisch sein, sodaß die Instantiierung wegfällt.

Bei der Verwenden sieht man dann aber auch genau, wo die Implementierung der Berechnung steht:

Procedure

Delphi-Quellcode:
  myVertex.DoSomeWeirdAndMagicStuff(someOtherVertix);
// vs.
  TTheSpecialCalculator.DoSomeWeirdAndMagicStuff(myVertex, someOtherVertix);


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:43 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