《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

相关文章

AI绘图怎么变现?想做点副业的小白必看!

在科技飞速发展的今天,AI绘图作为一种新兴技术,不仅改变了艺术创作的方式,也为创作者提供了多种变现途径。本文将详细探讨几种常见的AI绘图变现方式,帮助创作者更好地利用这一技术实现经济收益。 更多实操教程和AI绘画工具,可以扫描下方,免费获取 定制服务:个性化的创意商机 个性化定制 AI绘图技术能够根据用户需求生成个性化的头像、壁纸、插画等作品。例如,姓氏头像在电商平台上非常受欢迎,

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除