《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

相关文章

springboot security快速使用示例详解

《springbootsecurity快速使用示例详解》:本文主要介绍springbootsecurity快速使用示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录创www.chinasem.cn建spring boot项目生成脚手架配置依赖接口示例代码项目结构启用s

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

golang 日志log与logrus示例详解

《golang日志log与logrus示例详解》log是Go语言标准库中一个简单的日志库,本文给大家介绍golang日志log与logrus示例详解,感兴趣的朋友一起看看吧... 目录一、Go 标准库 log 详解1. 功能特点2. 常用函数3. 示例代码4. 优势和局限二、第三方库 logrus 详解1.

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Redis 中的热点键和数据倾斜示例详解

《Redis中的热点键和数据倾斜示例详解》热点键是指在Redis中被频繁访问的特定键,这些键由于其高访问频率,可能导致Redis服务器的性能问题,尤其是在高并发场景下,本文给大家介绍Redis中的热... 目录Redis 中的热点键和数据倾斜热点键(Hot Key)定义特点应对策略示例数据倾斜(Data S

JavaScript Array.from及其相关用法详解(示例演示)

《JavaScriptArray.from及其相关用法详解(示例演示)》Array.from方法是ES6引入的一个静态方法,用于从类数组对象或可迭代对象创建一个新的数组实例,本文将详细介绍Array... 目录一、Array.from 方法概述1. 方法介绍2. 示例演示二、结合实际场景的使用1. 初始化二

C#中的 StreamReader/StreamWriter 使用示例详解

《C#中的StreamReader/StreamWriter使用示例详解》在C#开发中,StreamReader和StreamWriter是处理文本文件的核心类,属于System.IO命名空间,本... 目录前言一、什么是 StreamReader 和 StreamWriter?1. 定义2. 特点3. 用

Java中&和&&以及|和||的区别、应用场景和代码示例

《Java中&和&&以及|和||的区别、应用场景和代码示例》:本文主要介绍Java中的逻辑运算符&、&&、|和||的区别,包括它们在布尔和整数类型上的应用,文中通过代码介绍的非常详细,需要的朋友可... 目录前言1. & 和 &&代码示例2. | 和 ||代码示例3. 为什么要使用 & 和 | 而不是总是使

Java强制转化示例代码详解

《Java强制转化示例代码详解》:本文主要介绍Java编程语言中的类型转换,包括基本类型之间的强制类型转换和引用类型的强制类型转换,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录引入基本类型强制转换1.数字之间2.数字字符之间引入引用类型的强制转换总结引入在Java编程语言中,类型转换(无论

redis+lua实现分布式限流的示例

《redis+lua实现分布式限流的示例》本文主要介绍了redis+lua实现分布式限流的示例,可以实现复杂的限流逻辑,如滑动窗口限流,并且避免了多步操作导致的并发问题,具有一定的参考价值,感兴趣的可... 目录为什么使用Redis+Lua实现分布式限流使用ZSET也可以实现限流,为什么选择lua的方式实现