类定义的属性和方法构成接口,该接口决定类用户如何与类的对象交互。创建一组相关类时,接口定义所有这些类的公共接口。接口的实际实现可能因类而异。
以一组设计用于表示各种图形类型的类为例。所有类都必须实现Data属性,以包含用于生成图形的数据。然而,对于不同类型的图形,数据的形式可能有很大不同。每个类可以通过不同方式实现 Data 属性。
对于方法,也存在同样的差异。所有类都可以有一个创建图形的draw方法,但是,该方法的实现会根据图形的类型而变化。
接口类的基本思想是指定每个子类必须实现的属性和方法,而不定义实际实现。这种方式使您能够对一组相关对象强制实施一致的接口。在将来添加更多的类时,接口始终保持不变。
此示例为用于表示特化图形的类创建一个接口。接口是抽象类,它定义子类必须实现的属性和方法,但不指定如何实现这些组件。这种方式强制实施一致的接口,同时提供必要的灵活性来以不同方式实现每个特化子类的内部工作。
在本示例中,包文件夹包含接口、派生的子类和工具函数:
+graphics/GraphInterface.m % abstract interface class
+graphics/LineGraph.m % concrete subclass
接口的属性和方法
graph 类指定以下属性,子类必须定义这些属性:
GraphInterface 类命名子类必须实现的三个抽象方法。GraphInterface 类还在注释中建议每个子类构造函数必须接受所有类属性的绘图数据和属性名称/属性值对组。
接口决定类设计
从 GraphInterface 抽象类派生的类包实现以下行为:
定义接口
GraphInterface 类是抽象类,用于定义子类使用的方法和属性。抽象类中的注释说明预期的实现:
classdef GraphInterface < handle
% Abstract class for creating data graphs
% Subclass constructor should accept
% the data that is to be plotted and
% property name/property value pairs
properties (SetAccess = protected, GetAccess = protected)
Primitive
AxesHandle
end
properties
Data
end
methods (Abstract)
draw(obj)
% Use a line, surface,
% or patch graphics primitive
zoom(obj,factor)
% Change the CameraViewAngle
% for 2D and 3D views
% use camzoom for consistency
updateGraph(obj)
% Update the Data property and
% update the drawing primitive
end
methods
function set.Data(obj,newdata)
obj.Data = newdata;
updateGraph(obj)
end
function addButtons(gobj)
hfig = get(gobj.AxesHandle,'Parent');
uicontrol(hfig,'Style','pushbutton','String','Zoom Out',...
'Callback',@(src,evnt)zoom(gobj,.5));
uicontrol(hfig,'Style','pushbutton','String','Zoom In',...
'Callback',@(src,evnt)zoom(gobj,2),...
'Position',[100 20 60 20]);
end
end
end
GraphInterface 类实现属性 set 方法 (set.Data) 以监控 Data 属性的更改。另一种方式是将 Data 属性定义为 Abstract,并使子类能够确定是否为此属性实现 set 访问方法。GraphInterface 类定义一个调用抽象方法(updateGraph,每个子类都必须实现该方法)的 set 访问方法。GraphInterface 接口为整个类包施加特定设计,而不会限制灵活性。
用于所有子类的方法
addButtons 方法为 zoom 方法添加普通按钮,每个子类都必须实现这些方法。通过使用方法而不是普通函数,可使 addButtons 能够访问受保护的类数据(坐标区句柄)。使用对象 zoom 方法作为普通按钮回调。
function addButtons(gobj)
hfig = get(gobj.AxesHandle,'Parent');
uicontrol(hfig,'Style','pushbutton',...
'String','Zoom Out',...
'Callback',@(src,evnt)zoom(gobj,.5));
uicontrol(hfig,'Style','pushbutton',...
'String','Zoom In',...
'Callback',@(src,evnt)zoom(gobj,2),...
'Position',[100 20 60 20]);
end
派生具体类 - LineGraph
此示例只定义一个用于表示简单线图的子类。它派生自 GraphInterface,但提供对抽象方法 draw、zoom、updateGraph 及其自己的构造函数的实现。基类 GraphInterface 和子类都包含在包 (graphics) 中,您必须使用它来引用类名:
classdef LineGraph < graphics.GraphInterface
添加属性
LineGraph 类实现在 GraphInterface 类中定义的接口,并添加两个附加属性 - LineColor 和 LineType。此类为每个属性定义初始值,因此在构造函数中指定属性值是可选的。可以创建没有数据的 LineGraph 对象,但无法从该对象生成图形。
properties
LineColor = [0 0 0];
LineType = '-';
end
LineGraph 构造函数
构造函数接受 struct 及 x 和 y 坐标数据,以及属性名称/属性值对组:
function gobj = LineGraph(data,varargin)
if nargin > 0
gobj.Data = data;
if nargin > 2
for k=1:2:length(varargin)
gobj.(varargin{k}) = varargin{k+1};
end
end
end
end
实现 draw 方法
LineGraph 的 draw 方法使用属性值创建一个 line 对象。LineGraph 类将 line 句柄存储为受保护的类数据。为了支持类构造函数不使用输入参数,draw 会在继续之前检查 Data 属性以确定它是否为空:
function gobj = draw(gobj)
if isempty(gobj.Data)
error('The LineGraph object contains no data')
end
h = line(gobj.Data.x,gobj.Data.y,...
'Color',gobj.LineColor,...
'LineStyle',gobj.LineType);
gobj.Primitive = h;
gobj.AxesHandle = get(h,'Parent');
end
实现 zoom 方法
LineGraph 的 zoom 方法使用 GraphInterface 类的注释中推荐的 camzoom 函数。camzoom 提供方便的接口来支持缩放功能,并使用 addButtons 方法创建的普通按钮以正确进行操作。
定义属性 set 方法
通过属性 set 方法,可以方便地在构造函数中首次更改属性值时自动执行代码。(请参阅 属性set方法)每当属性值更改时,linegraph 类都会使用 set 方法更新 line 原始数据(这会导致绘图重绘)。使用属性 set 方法可以快速更新数据图,而无需调用 draw 方法。draw 方法通过重置所有值为当前属性值来更新绘图。
三个属性使用 set 方法:LineColor、LineType 和 Data。LineColor 和 LineType 是由 LineGraph 类添加的属性,并且是该类使用的 line 基元特有的属性。其他子类可以定义对其特化所独有的不同属性(例如,FaceColor)。
GraphInterface 类实现 Data 属性 set 方法。但是,GraphInterface 类要求每个子类定义名为 updateGraph 的方法,该方法处理所使用的特定绘图基元的绘图数据更新。
LineGraph 类
以下是 LineGraph 类定义。
classdef LineGraph < graphics.GraphInterface
properties
LineColor = [0 0 0]
LineType = '-'
end
methods
function gobj = LineGraph(data,varargin)
if nargin > 0
gobj.Data = data;
if nargin > 1
for k=1:2:length(varargin)
gobj.(varargin{k}) = varargin{k+1};
end
end
end
end
function gobj = draw(gobj)
if isempty(gobj.Data)
error('The LineGraph object contains no data')
end
h = line(gobj.Data.x,gobj.Data.y,...
'Color',gobj.LineColor,...
'LineStyle',gobj.LineType);
gobj.Primitive = h;
gobj.AxesHandle = h.Parent;
end
function zoom(gobj,factor)
camzoom(gobj.AxesHandle,factor)
end
function updateGraph(gobj)
set(gobj.Primitive,...
'XData',gobj.Data.x,...
'YData',gobj.Data.y)
end
function set.LineColor(gobj,color)
gobj.LineColor = color;
set(gobj.Primitive,'Color',color)
end
function set.LineType(gobj,ls)
gobj.LineType = ls;
set(gobj.Primitive,'LineStyle',ls)
end
end
end
使用 LineGraph
类
LineGraph 类定义由 graph 基类指定的简单 API,并实现其特化类型的图形:
d.x = 1:10;
d.y = rand(10,1);
lg = graphics.LineGraph(d,'LineColor','b','LineType',':');
lg.draw;
lg.addButtons;
点击放大按钮会显示为该按钮提供回调的 zoom 方法。
更改属性会更新图形:
d.y = rand(10,1);
lg.Data = d;
lg.LineColor = [0.9,0.1,0.6];
现在点击缩小并查看新结果:
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删