《GOF设计模式》—策略(STRATEGY)—Delphi源码示例:文本换行

示例:文本换行
说明:
有许多算法可对一个正文流进行分行。将这些算法硬编进使用它们的类中是不可取的。我们可以定义一些类来封装不同的换行算法,从而避免这些问题。一个以这种方法封装的算法称为一个策略(strategy)。
clip_image002
代码:
 clip_image002[5]
unit uComposition;

interface

uses
    Windows, Classes, Graphics;

type
    TIntArray = array of integer;
    TCoord = TPoint;
    TCoords = array of TCoord;

    TCompositor = class;

    {文档元素}
    TComponent = class
    private
        FCanvas: TCanvas;
        FNatural: TCoord; //--正常大小(宽、高)
        FShrink: TCoord; //--可伸展性
        FStretch: TCoord; //--可收缩性
    public
        constructor Create(ACanvas: TCanvas; ANatural, AStretch, AShrink: TCoord);
        //---
        procedure Draw(ALineWidth: integer; ABreaks: TIntArray; ABreakCount: integer;
            var ABreakIndex: integer); virtual;
        //---
        property Natural: TCoord read FNatural;
        property Stretch: TCoord read FStretch;
        property Shrink: TCoord read FShrink;
    end;
    TComponents = array of TComponent;

    TTextComponent = class(TComponent)
    private
        FText: string;
    public
        constructor Create(ACanvas: TCanvas; AText: string; ANatural, AStretch, AShrink: TCoord);
        //---
        procedure Draw(ALineWidth: integer; ABreaks: TIntArray; ABreakCount: integer;
            var ABreakIndex: integer); override;
    end;
    TGraphComponent = class(TComponent)
    end;

    {文档}
    TComposition = class
    private
        FCompositor: TCompositor;
        FComponents: TComponents;
        FComponentCount: integer;
        //---
        FLineWidth: integer;
        FLineCount: integer;
        FLineBreaks: TIntArray;
    public
        constructor Create(ACompositor: TCompositor);
        destructor Destroy; override;
        //---
        procedure Add(AComponent: TComponent);
        procedure Repair();
        //---
        property LineWidth: integer read FLineWidth write FLineWidth;
    end;

    {换行策略}
    TCompositor = class
    public
        function Compose(ANatural, AStretch, AShrink: TCoords;
            AComponentCount, ALineWidth: integer; var ABreaks: TIntArray): integer; virtual; abstract;
    end;
    TSimpleComponentor = class(TCompositor)
    public
        function Compose(ANatural, AStretch, AShrink: TCoords;
            AComponentCount, ALineWidth: integer; var ABreaks: TIntArray): integer; override;
    end;
    TTeXComponentor = class(TCompositor)
    public
        function Compose(ANatural, AStretch, AShrink: TCoords;
            AComponentCount, ALineWidth: integer; var ABreaks: TIntArray): integer; override;
    end;
    TArrayComponentor = class(TCompositor)
    public
        function Compose(ANatural, AStretch, AShrink: TCoords;
            AComponentCount, ALineWidth: integer; var ABreaks: TIntArray): integer; override;
    end;

procedure Test(ACanvas: TCanvas);

implementation

uses Math;

const
    CNT_CHAR_WIDTH = 10;
    CNT_CHAR_Height = 20;

procedure Test(ACanvas: TCanvas);
var
    ACompositor: TCompositor;
    AComposition: TComposition;
    //---
    procedure _Add(const AText: string);
    var
        ATextWidth, ATextHeight: Integer;
    begin
        with AComposition do
        begin
            ATextWidth := Length(AText) * CNT_CHAR_WIDTH;
            ATextHeight := CNT_CHAR_Height;
            Add(TTextComponent.Create(
                ACanvas, AText,
                Point(ATextWidth, ATextHeight),
                Point(ATextWidth, ATextHeight - 5),
                Point(ATextWidth, ATextHeight + 5)
                ));
        end;
    end;
begin
    //ACompositor := TSimpleComponentor.Create;
    //ACompositor := TTeXComponentor.Create;
    ACompositor := TArrayComponentor.Create;
    AComposition := TComposition.Create(ACompositor);
    try
        _Add('aaaaaaaaaaaaaaaaaaaaa');
        _Add('bbbbbbbbbbbbbbbbbbbbb');
        _Add('aaaaaaaaaaaaaaaaaaaaaa');
        _Add('bbbbbbbbbbbbbbbbbbbbbbb');
        //---
        AComposition.LineWidth := 8 * CNT_CHAR_WIDTH;
        AComposition.Repair;
    finally
        AComposition.Free;
        ACompositor.Free;
    end;
end;

procedure TComposition.Add(AComponent: TComponent);
begin
    SetLength(FComponents, FComponentCount + 1);
    FComponents[FComponentCount] := AComponent;
    FComponentCount := Length(FComponents);
end;

constructor TComposition.Create(ACompositor: TCompositor);
begin
    FCompositor := ACompositor;
    FComponentCount := Length(FComponents);
end;

destructor TComposition.Destroy;
    //---
    procedure _ClearComponents;
    var
        i: Integer;
    begin
        for i := Low(FComponents) to High(FComponents) do
            FComponents[i].Free;
    end;
begin
    _ClearComponents;
    //---
    inherited;
end;

procedure TComposition.Repair();
var
    ANatural, AStretchability, AShrinkability: TCoords;
    AComponentCount: integer;
    ABreaks: TIntArray;
    ABreakCount: integer;
    //---
    procedure _InitParams;
    var
        i: Integer;
    begin
        AComponentCount := Length(FComponents);
        //---
        SetLength(ANatural, AComponentCount);
        SetLength(AStretchability, AComponentCount);
        SetLength(AShrinkability, AComponentCount);
        //---
        for i := Low(FComponents) to High(FComponents) do
        begin
            with FComponents[i] do
            begin
                ANatural[i] := Natural;
                AStretchability[i] := Stretch;
                AShrinkability[i] := Shrink;
            end;
        end;
    end;
    //---
    procedure _DrawComponents;
    var
        i, ALineIndex: integer;
    begin
        ALineIndex := 0;
        for i := Low(FComponents) to High(FComponents) do
            FComponents[i].Draw(FLineWidth, FLineBreaks, FLineCount, ALineIndex);
    end;
begin
    _InitParams;
    //---
    ABreakCount := FCompositor.Compose(ANatural, AStretchability, AShrinkability,
        AComponentCount, FLineWidth, ABreaks);
    FLineCount := ABreakCount;
    FLineBreaks := ABreaks;
    //---
    _DrawComponents;
end;

function TSimpleComponentor.Compose(ANatural, AStretch, AShrink: TCoords;
    AComponentCount, ALineWidth: integer; var ABreaks: TIntArray): integer;
var
    i,j,AComponentLineCount,ABreakY,ABreakCount:Integer;
    //---
    procedure _AddBreak;
    begin
        Inc(ABreakCount);
        SetLength(ABreaks,ABreakCount);
        //---
        ABreaks[ABreakCount - 1] := ABreakY;
    end;
begin
    ABreakCount := 0;
    ABreakY := 0;
    //---
    for i := 0 to AComponentCount - 1 do
    begin
        with ANatural[i] do
        begin
            AComponentLineCount := Ceil(X / ALineWidth);
            for j := 0 to AComponentLineCount - 1 do
            begin
                _AddBreak;
                ABreakY := ABreakY + Y;
            end;
        end;
    end;
    //---
    Result := ABreakCount;
end;

function TTeXComponentor.Compose(ANatural, AStretch, AShrink: TCoords;
    AComponentCount, ALineWidth: integer; var ABreaks: TIntArray): integer;
var
    i,j,AComponentLineCount,ABreakY,ABreakCount:Integer;
    //---
    procedure _AddBreak;
    begin
        Inc(ABreakCount);
        SetLength(ABreaks,ABreakCount);
        //---
        ABreaks[ABreakCount - 1] := ABreakY;
    end;
begin
    ABreakCount := 0;
    ABreakY := 0;
    //---
    for i := 0 to AComponentCount - 1 do
    begin
        with ANatural[i] do
            AComponentLineCount := Ceil(X / ALineWidth);
        //---
        for j := 0 to AComponentLineCount - 2 do
        begin
            _AddBreak;
            ABreakY := ABreakY + AStretch[i].Y;
        end;
        //---
        if AComponentLineCount - 1 >= 0 then
        begin
            _AddBreak;
            ABreakY := ABreakY + AShrink[i].Y;
        end;
    end;
    //---
    Result := ABreakCount;
end;

function TArrayComponentor.Compose(ANatural, AStretch, AShrink: TCoords;
    AComponentCount, ALineWidth: integer; var ABreaks: TIntArray): integer;
const
    CNT_LineHeight = 25;
var
    i,j,AComponentLineCount,ABreakY,ABreakCount:Integer;
    //---
    procedure _AddBreak;
    begin
        Inc(ABreakCount);
        SetLength(ABreaks,ABreakCount);
        //---
        ABreaks[ABreakCount - 1] := ABreakY;
    end;
begin
    ABreakCount := 0;
    ABreakY := 0;
    //---
    for i := 0 to AComponentCount - 1 do
    begin
        with ANatural[i] do
            AComponentLineCount := Ceil(X / ALineWidth);
        for j := 0 to AComponentLineCount - 1 do
        begin
            _AddBreak;
            ABreakY := ABreakY + CNT_LineHeight;
        end;
    end;
    //---
    Result := ABreakCount;
end;

constructor TComponent.Create(ACanvas: TCanvas; ANatural, AStretch, AShrink: TCoord);
begin
    FCanvas := ACanvas;
    FNatural := ANatural;
    FStretch := AStretch;
    FShrink := AShrink;
end;

procedure TComponent.Draw(ALineWidth: integer; ABreaks: TIntArray; ABreakCount:
    integer; var ABreakIndex: integer);
begin

end;

constructor TTextComponent.Create(ACanvas: TCanvas; AText: string; ANatural, AStretch,
    AShrink: TCoord);
begin
    inherited Create(ACanvas, ANatural, AStretch, AShrink);
    //---
    FText := AText;
end;

procedure TTextComponent.Draw(ALineWidth: integer; ABreaks: TIntArray;
    ABreakCount: integer; var ABreakIndex: integer);
var
    i, ALineCount, ALineCharCount: Integer;
    ALineText: string;
begin
    if ABreakIndex >= ABreakCount then
        Exit;
    //---
    ALineCharCount := ALineWidth div CNT_CHAR_WIDTH;
    ALineCount := Ceil(Length(FText) / ALineCharCount);
    //---
    with FCanvas do
    begin
        with Font do
        begin
            Name := '宋体';
            Size := 11;
            Color := clRed;
        end;
        //---
        for i := 0 to ALineCount - 1 do
        begin
            ALineText := Copy(FText, ALineCharCount * i + 1, ALineCharCount);
            TextOut(10, ABreaks[ABreakIndex], ALineText);
            //---
            Inc(ABreakIndex);
        end;
    end;
end;

end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值