Main Content

インターフェイスのスーパークラスの定義

インターフェイス

クラスで定義されたプロパティとメソッドは、クラスのユーザーがそのクラスのオブジェクトどのようにやり取りするかを決めるインターフェイスを形成します。関連するクラスのグループを作成する場合には、インターフェイスでこれらのクラスすべてに共通のインターフェイスを定義します。インターフェイスの実際の実装がクラスによって異なっていてもかまいません。

さまざまな種類のグラフを表すために設計されたクラスのセットを考えます。すべてのクラスで、グラフの作成に使用するデータを含む Data プロパティを実装しなければなりません。ただし、データの形式は、グラフの種類によって大きく異なります。それぞれのクラスに、異なる方法で Data プロパティを実装できます。

同様の違いはメソッドに適用されます。すべてのクラスはグラフを作成する draw メソッドをもつことができますが、このメソッドの実装はグラフの種類によって異なります。

インターフェイス クラスの基本的な考え方は、各サブクラスが実際の実装を定義せずに実装すべきプロパティとメソッドを指定することです。このようにすると、関連するオブジェクトのグループで使用するインターフェイスが統一されます。後でクラスを追加しても、インターフェイスは変化しません。

グラフを実装するインターフェイス クラス

この例では、特化したグラフを表すために使用するクラスのインターフェイスを作成します。このインターフェイスは、サブクラスで実装する必要のあるプロパティとメソッドを定義する抽象クラスです。

ただし、これらのコンポーネントをどのように実装するかは指定しません。このため、統一のインターフェイスを使用しても、特化したサブクラスごとに異なる機能を実装できる柔軟性が提供されます。

この例では、名前空間フォルダーに次のようなインターフェイス、派生したサブクラス、ユーティリティ関数が含まれます。

+graphics/GraphInterface.m  % abstract interface class
+graphics/LineGraph.m       % concrete subclass

インターフェイスのプロパティとメソッド

GraphInterface クラスは次のプロパティを指定します。これらのプロパティは、サブクラスで定義しなければなりません。

  • Primitive — 特化したグラフの実装に使用されるグラフィックス オブジェクトのハンドル。クラスのユーザーは、これらのオブジェクトに直接アクセスする必要がないので、このプロパティの SetAccess 属性と GetAccess 属性は protected です。

  • AxesHandle — グラフに使用する座標軸のハンドル。特化した graph オブジェクトでは、axes オブジェクトのプロパティを設定できます。このプロパティには protected SetAccessGetAccess があります。

  • DataGraphInterface クラスのすべてのサブクラスはデータを保存しなければなりません。データの種類が異なるので、各サブクラスでデータのストレージ方法を定義します。サブクラスのユーザーは、このプロパティがパブリックのアクセス権をもつようにデータ値を変更できます。

GraphInterface クラスは、サブクラスで実装しなければならない 3 つの抽象メソッドの名前を指定します。また、GraphInterface クラスのコメントには、各サブクラスのコンストラクターが、プロットするデータと、すべてのクラス プロパティの名前と値のペアを受け入れなければならないということが示唆されています。

  • サブクラス コンストラクター — データと、プロパティ/値のペアを受け入れ、オブジェクトを返します。

  • draw — サブクラスが実装するグラフのタイプに従って、基本のレンダリングを作成し、データをグラフ表示するために使用します。

  • zoom — Axes の CameraViewAngle プロパティを変更することによる zoom メソッドの実装。このインターフェイスは、サブクラス間の整合のために関数 camzoom を使用するように表示します。addButtons 静的メソッドで作成されたズーム ボタンは、このメソッドをコールバックとして使用します。

  • updateGraphData プロパティが変更されるたびに、プロットされるデータを更新するために 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 から派生しますが、抽象メソッド drawzoomupdateGraph、およびサブクラス自身のコンストラクターを実装します。基底クラス (GraphInterface) とサブクラスはすべて名前空間 (graphics) に含めます。この名前空間は、クラス名を参照するときに使用しなければなりません。

classdef LineGraph < graphics.GraphInterface

プロパティの追加

LineGraph クラスは、GraphInterface クラスで定義されるインターフェイスを実装して 2 つのプロパティ (LineColorLineType) を追加します。このクラスは、各プロパティの初期値を定義します。このため、コンストラクターでのプロパティ値の指定はオプションになります。データがなくても 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 メソッドの実装

LineGraphzoom メソッドは、GraphInterface クラスにあるコメントに従って、関数 camzoom を使用します。関数 camzoom は、ズームのための便利なインターフェイスを提供し、addButtons メソッドによって作成されるプッシュ ボタンと共に正しく動作します。

プロパティの set メソッドの定義

プロパティの set メソッドは、コンストラクターのプロパティ値が初めて変更されたときに、自動的にコードが実行されるようにする便利な方法の 1 つです(プロパティの get メソッドおよび set メソッドを参照)。linegraph クラスは、プロパティ値が変更されるたびに set メソッドを使用して、始めの line データを更新します。プロットは、再描画されます。プロパティの set メソッドを使用すると、draw メソッドを呼び出さなくてもデータのプロットをすばやく更新できます。一方、draw メソッドは、現在のプロパティの値に合わせてすべての値をリセットして、プロットを更新します。

LineColorLineType および Data の 3 つのプロパティで set メソッドを使用します。LineColorLineType は、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 メソッドを表示します。

Window showing randomly drawn line segments

プロパティを変更すると、グラフが更新されます。

d.y = rand(10,1); 
lg.Data = d;
lg.LineColor = [0.9,0.1,0.6]; 

ここで、[ズーム アウト] をクリックして新しい結果を参照します。

Window showing randomly drawn line segments

関連するトピック