《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

相关文章

利用c++判断水仙花数并输出示例代码

《利用c++判断水仙花数并输出示例代码》水仙花数是指一个三位数,其各位数字的立方和恰好等于该数本身,:本文主要介绍利用c++判断水仙花数并输出的相关资料,文中通过代码介绍的非常详细,需要的朋友可以... 以下是使用C++实现的相同逻辑代码:#include <IOStream>#include <vec

SQL Server 中的表进行行转列场景示例

《SQLServer中的表进行行转列场景示例》本文详细介绍了SQLServer行转列(Pivot)的三种常用写法,包括固定列名、条件聚合和动态列名,文章还提供了实际示例、动态列数处理、性能优化建议... 目录一、常见场景示例二、写法 1:PIVOT(固定列名)三、写法 2:条件聚合(CASE WHEN)四、

Java 接口定义变量的示例代码

《Java接口定义变量的示例代码》文章介绍了Java接口中的变量和方法,接口中的变量必须是publicstaticfinal的,用于定义常量,而方法默认是publicabstract的,必须由实现类... 在 Java 中,接口是一种抽象类型,用于定义类必须实现的方法。接口可以包含常量和方法,但不能包含实例

使用Redis实现会话管理的示例代码

《使用Redis实现会话管理的示例代码》文章介绍了如何使用Redis实现会话管理,包括会话的创建、读取、更新和删除操作,通过设置会话超时时间并重置,可以确保会话在用户持续活动期间不会过期,此外,展示了... 目录1. 会话管理的基本概念2. 使用Redis实现会话管理2.1 引入依赖2.2 会话管理基本操作

mybatis-plus分表实现案例(附示例代码)

《mybatis-plus分表实现案例(附示例代码)》MyBatis-Plus是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生,:本文主要介绍my... 目录文档说明数据库水平分表思路1. 为什么要水平分表2. 核心设计要点3.基于数据库水平分表注意事项示例

Mybatis的mapper文件中#和$的区别示例解析

《Mybatis的mapper文件中#和$的区别示例解析》MyBatis的mapper文件中,#{}和${}是两种参数占位符,核心差异在于参数解析方式、SQL注入风险、适用场景,以下从底层原理、使用场... 目录MyBATis 中 mapper 文件里 #{} 与 ${} 的核心区别一、核心区别对比表二、底

HTML5的input标签的`type`属性值详解和代码示例

《HTML5的input标签的`type`属性值详解和代码示例》HTML5的`input`标签提供了多种`type`属性值,用于创建不同类型的输入控件,满足用户输入的多样化需求,从文本输入、密码输入、... 目录一、引言二、文本类输入类型2.1 text2.2 password2.3 textarea(严格

MySQL中between and的基本用法、范围查询示例详解

《MySQL中betweenand的基本用法、范围查询示例详解》BETWEENAND操作符在MySQL中用于选择在两个值之间的数据,包括边界值,它支持数值和日期类型,示例展示了如何使用BETWEEN... 目录一、between and语法二、使用示例2.1、betwphpeen and数值查询2.2、be

python中的flask_sqlalchemy的使用及示例详解

《python中的flask_sqlalchemy的使用及示例详解》文章主要介绍了在使用SQLAlchemy创建模型实例时,通过元类动态创建实例的方式,并说明了如何在实例化时执行__init__方法,... 目录@orm.reconstructorSQLAlchemy的回滚关联其他模型数据库基本操作将数据添

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配