《GOF设计模式》—适配器(ADAPTER)—Delphi源码示例:绘图编辑器

本文主要是介绍《GOF设计模式》—适配器(ADAPTER)—Delphi源码示例:绘图编辑器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


示例:绘图编辑器

说明:

有时,为复用而设计的工具箱类不能够被复用的原因仅仅是因为它的接口与专业应用领域所需要的接口不匹配。

例如,有一个绘图编辑器,这个编辑器允许用户绘制和排列基本图元(线、多边型和正

文等)、生成图片和图表。这个绘图编辑器的关键抽象是图形对象。图形对象有一个可编辑的形状,并可以绘制自身。图形对象的接口由一个称为Shape的抽象类定义。绘图编辑器为每一种图形对象定义了一个Shape的子类:LineShape类对应于直线,PolygonShape类对应于多边型,等等。

LineShapePolygonShape这样的基本几何图形的类比较容易实现,这是由于它们的绘图和编辑功能本来就很有限。但是对于可以显示和编辑正文的TextShape子类来说,实现相当困难,因为即使是基本的正文编辑也要涉及到复杂的屏幕刷新和缓冲区管理。同时,成品的用户界面工具箱可能已经提供了一个复杂的TextView类用于显示和编辑正文。理想的情况是我们可以复用这个TextView类以实现TextShape类,但是工具箱的设计者当时并没有考虑Shape的存在,因此TextViewShape对象不能互换。

一个应用可能会有一些类具有不同的接口并且这些接口互不兼容,在这样的应用中象TextView这样已经存在并且不相关的类如何协同工作呢?我们可以改变TextView类使它兼容Shape类的接口,但前提是必须有这个工具箱的源代码。然而即使我们得到了这些源代码,修改TextView也是没有什么意义的;因为不应该仅仅为了实现一个应用,工具箱就不得不采用一些与特定领域相关的接口。

我们可以不用上面的方法,而定义一个TextShape类,由它来适配TextView的接口和Shape的接口。我们可以用两种方法做这件事:1)、继承Shape类的接口和TextView的实现,或2)、将一个TextView实例作为TextShape的组成部分,并且使用TextView的接口实现TextShape。这两种方法恰恰对应于Adapter模式的类和对象版本。我们将TextShape称之为适配器Adapter

上面的类图说明了对象适配器实例。它说明了在Shape类中声明的BoundingBox请求如何被转换成在TextView类中定义的GetExtent请求。由于TextShapeTextView的接口与Shape的接口进行了匹配,因此绘图编辑器就可以复用原先并不兼容的TextView类。

Adapter时常还要负责提供那些被匹配的类所没有提供的功能,上面的类图中说明了适配器如何实现这些职责。由于绘图编辑器允许用户交互的将每一个Shape对象“拖动”到一个新的位置,而TextView设计中没有这种功能。我们可以实现TextShape类的CreateManipulator操作,从而增加这个缺少的功能,这个操作返回相应的Manipulator子类的一个实例。

Manipulator是一个抽象类,它所描述的对象知道如何驱动Shape类响应相应的用户输入,例如将图形拖动到一个新的位置。对应于不同形状的图形,Manipulator有不同的子类;例如子类TextManipulator对应于TextShapeTextShape通过返回一个TextManipulator实例,增加了TextView中缺少而Shape需要的功能。

从类ShapeTextView开始,我们将给出类适配器和对象适配器实现代码的简要框架。Shape假定有一个边框,这个边框由它相对的两角定义。而TextView则由原点、宽度和高度定义。Shape同时定义了CreateManipulator操作用于创建一个Manipulator对象。当用户操作一个图形时,Manipulator对象知道如何驱动这个图形。TextView没有等同的操作。TextShape类是这些不同接口间的适配器。

对象适配器采用对象组合的方法将具有不同接口的类组合在一起。在该方法中,适配器TextShape维护一个指向TextView的指针。TextShape必须在构造器中对指向TextView实例的指针进行初始化,当它自身的操作被调用时,它还必须对它的TextView对象调用相应的操作。在本例中,假设客户创建了TextView对象并且将其传递给TextShape的构造器。

 

代码:

 

 

unit uDrawingEdtior;

 

interface

 

uses

    Windows,Classes,Graphics;

 

type

    TManipulator = class;

    TTextView = class;

 

    {图形对象}

    TShape = class

    private

        FCanvas: TCanvas;

    public

        {获取图形边界}

        procedure BoundingBox(var LeftTop,RightBottom: TPoint); virtual; abstract;

        {获取图形操作对象:工厂方法}

        function CreateManipulator(): TManipulator; virtual; abstract;

        procedure Draw; virtual; abstract;

        //---

        property Canvas: TCanvas read FCanvas write FCanvas;

    end;

    {直线}

    TLineShape = class(TShape)

    private

        FLeftTop,FRightBottom: TPoint;

    public

        constructor Create;

        //---

        procedure BoundingBox(var LeftTop,RightBottom: TPoint); override;

        function CreateManipulator: TManipulator; override;

        procedure Draw; override;

    end;

    {多边型}

    TPolygonShape = class(TShape)

    private

        FLeftTop,FRightBottom: TPoint;

    public

        constructor Create;

        //---

        procedure BoundingBox(var LeftTop,RightBottom: TPoint); override;

        function CreateManipulator: TManipulator; override;

        procedure Draw; override;

    end;

    {文本}

    TTextShape = class(TShape)

    private

        FTextView: TTextView;

    public

        constructor Create(ATextView: TTextView);

        //---

        procedure BoundingBox(var LeftTop,RightBottom: TPoint); override;

        function CreateManipulator(): TManipulator; override;

        procedure Draw; override;

        function IsEmpty: Boolean;

    end;

 

    {图形操作对象:依据使用端控制驱动图形对象}

    TManipulator = class

    private

        FShape: TShape;

    public

        constructor Create(AShape: TShape);

        //---

        procedure Operate; virtual; abstract;

    end;

    TLineManipulator = class(TManipulator)

    public

        procedure Operate; override;

    end;

    TPolygonManipulator = class(TManipulator)

    public

        procedure Operate; override;

    end;

    TTextManipulator = class(TManipulator)

    public

        procedure Operate; override;

    end;

 

    {文本视图}

    TTextView = class

    public

        {获取原点}

        procedure GetOrigin(var X,Y: Longint);

        {获取高宽:定义显示对象自原点计算之高及宽}

        procedure GetExtent(var width,height: Longint);

        function IsEmpty(): Boolean;

    end;

 

procedure ClearBackground(ACanvas: TCanvas);

 

implementation

 

procedure ClearBackground(ACanvas: TCanvas);

begin

    with ACanvas do

    begin

        with Brush do

        begin

            Color := clBlack;

            Style := bsSolid;

        end;

        FillRect(ClipRect);

    end;

end;

 

constructor TManipulator.Create(AShape: TShape);

begin

    FShape := AShape;

end;

 

procedure TTextView.GetOrigin(var X,Y: Longint);

begin

    X := 20;

    Y := 80;

end;

 

procedure TTextView.GetExtent(var width,height: Longint);

begin

    width := 10;

    height := 10;

end;

 

function TTextView.IsEmpty(): Boolean;

begin

    Result := True;

end;

 

constructor TTextShape.Create(ATextView: TTextView);

begin

    inherited Create;

    //---

    FTextView := ATextView;

end;

 

procedure TTextShape.BoundingBox(var LeftTop,RightBottom: TPoint);

var

    left,top,width,height: integer;

begin

    FTextView.GetOrigin(left,top);

    FTextView.GetExtent(width,height);

    //---

    LeftTop.x := left;

    LeftTop.y := top;

    //---

    RightBottom.x := left + width;

    RightBottom.y := top + height;

end;

 

function TTextShape.CreateManipulator(): TManipulator;

begin

    result := TTextManipulator.Create(self);

end;

 

procedure TTextShape.Draw;

var

    X,Y: integer;

begin

    FTextView.GetOrigin(X,Y);

    with self.Canvas do

    begin

        Font.Color := clYellow;

        TextOut(X,Y, '123');

    end;

end;

 

function TTextShape.IsEmpty: Boolean;

begin

    result := FTextView.IsEmpty;

end;

 

procedure TLineShape.BoundingBox(var LeftTop,RightBottom: TPoint);

begin

    LeftTop := FLeftTop;

    RightBottom := FRightBottom;

end;

 

constructor TLineShape.Create;

begin

    inherited;

    //---

    FLeftTop.x := 10;

    FLeftTop.y := 10;

    FRightBottom.x := FLeftTop.x + 20;

    FRightBottom.y := FLeftTop.y + 20;

end;

 

function TLineShape.CreateManipulator: TManipulator;

begin

    result := TLineManipulator.Create(self);

end;

 

procedure TLineShape.Draw;

begin

    with self.Canvas do

    begin

        with Pen do

        begin

            Color := clYellow;

            Style := psSolid;

            Width := 1;

            Mode := pmXor;

        end;

        //---

        with FLeftTop do

            MoveTo(X,Y);

        with FRightBottom do

            LineTo(X,Y);

    end;

end;

 

constructor TPolygonShape.Create;

begin

    inherited;

    //---

    FLeftTop.x := 50;

    FLeftTop.y := 50;

    FRightBottom.x := FLeftTop.x + 40;

    FRightBottom.y := FLeftTop.y + 40;

end;

 

procedure TPolygonShape.BoundingBox(var LeftTop,RightBottom: TPoint);

begin

    LeftTop := FLeftTop;

    RightBottom := FRightBottom;

end;

 

function TPolygonShape.CreateManipulator: TManipulator;

begin

    result := TPolygonManipulator.Create(self);

end;

 

procedure TPolygonShape.Draw;

var

    pts: array[0..2] of TPoint;

begin

    with self.Canvas do

    begin

        with Pen do

        begin

            Color := clYellow;

            Style := psSolid;

            Width := 1;

            Mode := pmXor;

        end;

        //---

        pts[0] := FLeftTop;

        pts[1].X := FRightBottom.X;

        pts[1].Y := FLeftTop.Y;

        pts[2] := FRightBottom;

        //---

        Polygon(pts);

    end;

end;

 

procedure TLineManipulator.Operate;

var

    LeftTop,RightBottom: TPoint;

begin

    with FShape.Canvas do

    begin

        with Pen do

        begin

            Width := 1;

            Color := clRed;

            Style := psSolid;

            Mode := pmXor;

        end;

        Brush.Style := bsClear;

        //---

        FShape.BoundingBox(LeftTop,RightBottom);

        Rectangle(LeftTop.x,LeftTop.y,RightBottom.x,RightBottom.y);

    end;

end;

 

procedure TPolygonManipulator.Operate;

var

    LeftTop,RightBottom: TPoint;

begin

    with FShape.Canvas do

    begin

        with Pen do

        begin

            Width := 1;

            Color := clRed;

            Style := psDot;

            Mode := pmXor;

        end;

        Brush.Style := bsClear;

        //---

        FShape.BoundingBox(LeftTop,RightBottom);

        Rectangle(LeftTop.x,LeftTop.y,RightBottom.x,RightBottom.y);

    end;

end;

 

procedure TTextManipulator.Operate;

var

    LeftTop,RightBottom: TPoint;

begin

    with FShape.Canvas do

    begin

        with Pen do

        begin

            Width := 1;

            Color := clRed;

            Style := psDashDotDot;

            Mode := pmXor;

        end;

        Brush.Style := bsClear;

        //---

        FShape.BoundingBox(LeftTop,RightBottom);

        Rectangle(LeftTop.x,LeftTop.y,RightBottom.x,RightBottom.y);

    end;

end;

 

end.

 

unit Unit1;

 

interface

 

uses

    Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,

    Dialogs,StdCtrls,ExtCtrls;

 

type

    TForm1 = class(TForm)

        Button1: TButton;

        Image1: TImage;

        procedure Button1Click(Sender: TObject);

    private

    { Private declarations }

    public

    { Public declarations }

    end;

 

var

    Form1: TForm1;

 

implementation

 

uses uAdapter,uDrawingEdtior;

 

{$R *.dfm}

 

procedure TForm1.Button1Click(Sender: TObject);

    //---

    procedure _DrawShape(AShape: TShape);

    var

        AManipulator: TManipulator;

    begin

        AShape.Canvas := Image1.Canvas;

        AShape.Draw;

        //---

        AManipulator := AShape.CreateManipulator;

        AManipulator.Operate;

        AManipulator.Free;

        //---

        AShape.Free;

    end;

var

    ATextView: TTextView;

begin

    ClearBackground(Image1.Canvas);

    //---

    _DrawShape(TLineShape.Create);

    _DrawShape(TPolygonShape.Create);

    //---

    ATextView := TTextView.Create;

    _DrawShape(TTextShape.Create(ATextView));

    ATextView.Free;

end;

 

end.

这篇关于《GOF设计模式》—适配器(ADAPTER)—Delphi源码示例:绘图编辑器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1142330

相关文章

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

Python中的Walrus运算符分析示例详解

《Python中的Walrus运算符分析示例详解》Python中的Walrus运算符(:=)是Python3.8引入的一个新特性,允许在表达式中同时赋值和返回值,它的核心作用是减少重复计算,提升代码简... 目录1. 在循环中避免重复计算2. 在条件判断中同时赋值变量3. 在列表推导式或字典推导式中简化逻辑

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

pandas中位数填充空值的实现示例

《pandas中位数填充空值的实现示例》中位数填充是一种简单而有效的方法,用于填充数据集中缺失的值,本文就来介绍一下pandas中位数填充空值的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是中位数填充?为什么选择中位数填充?示例数据结果分析完整代码总结在数据分析和机器学习过程中,处理缺失数

Pandas统计每行数据中的空值的方法示例

《Pandas统计每行数据中的空值的方法示例》处理缺失数据(NaN值)是一个非常常见的问题,本文主要介绍了Pandas统计每行数据中的空值的方法示例,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是空值?为什么要统计空值?准备工作创建示例数据统计每行空值数量进一步分析www.chinasem.cn处

利用Python调试串口的示例代码

《利用Python调试串口的示例代码》在嵌入式开发、物联网设备调试过程中,串口通信是最基础的调试手段本文将带你用Python+ttkbootstrap打造一款高颜值、多功能的串口调试助手,需要的可以了... 目录概述:为什么需要专业的串口调试工具项目架构设计1.1 技术栈选型1.2 关键类说明1.3 线程模

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

Android实现在线预览office文档的示例详解

《Android实现在线预览office文档的示例详解》在移动端展示在线Office文档(如Word、Excel、PPT)是一项常见需求,这篇文章为大家重点介绍了两种方案的实现方法,希望对大家有一定的... 目录一、项目概述二、相关技术知识三、实现思路3.1 方案一:WebView + Office Onl

Mysql用户授权(GRANT)语法及示例解读

《Mysql用户授权(GRANT)语法及示例解读》:本文主要介绍Mysql用户授权(GRANT)语法及示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql用户授权(GRANT)语法授予用户权限语法GRANT语句中的<权限类型>的使用WITH GRANT

Java中Scanner的用法示例小结

《Java中Scanner的用法示例小结》有时候我们在编写代码的时候可能会使用输入和输出,那Java也有自己的输入和输出,今天我们来探究一下,对JavaScanner用法相关知识感兴趣的朋友一起看看吧... 目录前言一 输出二 输入Scanner的使用多组输入三 综合练习:猜数字游戏猜数字前言有时候我们在