インターフェイスのスーパークラスの定義
インターフェイス
クラスで定義されたプロパティとメソッドは、クラスのユーザーがそのクラスのオブジェクトどのようにやり取りするかを決めるインターフェイスを形成します。関連するクラスのグループを作成する場合には、インターフェイスでこれらのクラスすべてに共通のインターフェイスを定義します。インターフェイスの実際の実装がクラスによって異なっていてもかまいません。
さまざまな種類のグラフを表すために設計されたクラスのセットを考えます。すべてのクラスで、グラフの作成に使用するデータを含む Data
プロパティを実装しなければなりません。ただし、データの形式は、グラフの種類によって大きく異なります。それぞれのクラスに、異なる方法で Data
プロパティを実装できます。
同様の違いはメソッドに適用されます。すべてのクラスはグラフを作成する draw
メソッドをもつことができますが、このメソッドの実装はグラフの種類によって異なります。
インターフェイス クラスの基本的な考え方は、各サブクラスが実際の実装を定義せずに実装すべきプロパティとメソッドを指定することです。このようにすると、関連するオブジェクトのグループで使用するインターフェイスが統一されます。後でクラスを追加しても、インターフェイスは変化しません。
グラフを実装するインターフェイス クラス
この例では、特化したグラフを表すために使用するクラスのインターフェイスを作成します。このインターフェイスは、サブクラスで実装する必要のあるプロパティとメソッドを定義する抽象クラスです。
ただし、これらのコンポーネントをどのように実装するかは指定しません。このため、統一のインターフェイスを使用しても、特化したサブクラスごとに異なる機能を実装できる柔軟性が提供されます。
この例では、名前空間フォルダーに次のようなインターフェイス、派生したサブクラス、ユーティリティ関数が含まれます。
+graphics/GraphInterface.m % abstract interface class +graphics/LineGraph.m % concrete subclass
インターフェイスのプロパティとメソッド
GraphInterface
クラスは次のプロパティを指定します。これらのプロパティは、サブクラスで定義しなければなりません。
Primitive
— 特化したグラフの実装に使用されるグラフィックス オブジェクトのハンドル。クラスのユーザーは、これらのオブジェクトに直接アクセスする必要がないので、このプロパティのSetAccess
属性とGetAccess
属性はprotected
です。AxesHandle
— グラフに使用する座標軸のハンドル。特化したgraph
オブジェクトでは、axes オブジェクトのプロパティを設定できます。このプロパティにはprotected
SetAccess
とGetAccess
があります。Data
—GraphInterface
クラスのすべてのサブクラスはデータを保存しなければなりません。データの種類が異なるので、各サブクラスでデータのストレージ方法を定義します。サブクラスのユーザーは、このプロパティがパブリックのアクセス権をもつようにデータ値を変更できます。
GraphInterface
クラスは、サブクラスで実装しなければならない 3 つの抽象メソッドの名前を指定します。また、GraphInterface
クラスのコメントには、各サブクラスのコンストラクターが、プロットするデータと、すべてのクラス プロパティの名前と値のペアを受け入れなければならないということが示唆されています。
サブクラス コンストラクター — データと、プロパティ/値のペアを受け入れ、オブジェクトを返します。
draw
— サブクラスが実装するグラフのタイプに従って、基本のレンダリングを作成し、データをグラフ表示するために使用します。zoom
— Axes のCameraViewAngle
プロパティを変更することによる zoom メソッドの実装。このインターフェイスは、サブクラス間の整合のために関数camzoom
を使用するように表示します。addButtons
静的メソッドで作成されたズーム ボタンは、このメソッドをコールバックとして使用します。updateGraph
—Data
プロパティが変更されるたびに、プロットされるデータを更新するためにset.Data
メソッドによって呼び出されるメソッド。
インターフェイス ガイド クラスの設計
GraphInterface
抽象クラスから派生するクラスの名前空間は、以下の動作を実装します。
プロットをレンダリングせずに特化した
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
この例では、簡単な線グラフを表示するために使用するサブクラスを 1 つだけ定義します。このサブクラスは、GraphInterface
から派生しますが、抽象メソッド draw
、zoom
、updateGraph
、およびサブクラス自身のコンストラクターを実装します。基底クラス (GraphInterface
) とサブクラスはすべて名前空間 (graphics
) に含めます。この名前空間は、クラス名を参照するときに使用しなければなりません。
classdef LineGraph < graphics.GraphInterface
プロパティの追加
LineGraph
クラスは、GraphInterface
クラスで定義されるインターフェイスを実装して 2 つのプロパティ (LineColor
と LineType
) を追加します。このクラスは、各プロパティの初期値を定義します。このため、コンストラクターでのプロパティ値の指定はオプションになります。データがなくても LineGraph
オブジェクトを作成できますが、このオブジェクトからグラフを作成することはできません。
properties
LineColor = [0 0 0];
LineType = '-';
end
LineGraph コンストラクター
コンストラクターは、x
座標および y
座標のデータをもつ struct
と、プロパティの名前と値のペアを受け入れます。
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 メソッドは、コンストラクターのプロパティ値が初めて変更されたときに、自動的にコードが実行されるようにする便利な方法の 1 つです(プロパティの get メソッドおよび set メソッドを参照)。linegraph
クラスは、プロパティ値が変更されるたびに set メソッドを使用して、始めの line
データを更新します。プロットは、再描画されます。プロパティの set メソッドを使用すると、draw
メソッドを呼び出さなくてもデータのプロットをすばやく更新できます。一方、draw
メソッドは、現在のプロパティの値に合わせてすべての値をリセットして、プロットを更新します。
LineColor
、LineType
および Data
の 3 つのプロパティで set メソッドを使用します。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];
ここで、[ズーム アウト] をクリックして新しい結果を参照します。