Main Content

인터페이스 슈퍼클래스 정의하기

인터페이스

클래스가 정의하는 속성과 메서드는 클래스 사용자가 클래스의 객체와 상호 작용하는 방식을 결정하는 인터페이스를 형성합니다. 관련된 클래스 그룹을 생성하면 인터페이스가 이 그룹의 모든 클래스에 공통되는 인터페이스를 정의합니다. 인터페이스에 대한 실제 구현은 클래스마다 다를 수 있습니다.

다양한 유형의 그래프를 표현하도록 설계된 클래스 집합이 있다고 가정하겠습니다. 모든 클래스는 Data 속성을 구현하여 그래프 생성에 사용되는 데이터를 포함해야 합니다. 하지만 데이터의 형태는 그래프 유형마다 상당히 다를 수 있습니다. 클래스마다 Data 속성을 각기 다르게 구현할 수 있습니다.

메서드 역시 이러한 차이를 가집니다. 모든 클래스에 그래프를 생성하는 draw 메서드가 있을 수 있는데 이 메서드에 대한 구현은 그래프 유형에 따라 다를 수 있습니다.

인터페이스 클래스의 기본 취지는 서브클래스가 반드시 구현해야 하는 속성 및 메서드를 실제 구현을 정의하지 않고 지정해 두는 것입니다. 이 취지를 잘 적용하면 관련된 객체 그룹에 일관된 인터페이스를 적용할 수 있습니다. 향후에 클래스를 더 추가해도 인터페이스는 그대로 유지됩니다.

그래프를 구현하는 인터페이스 클래스

이 예제에서는 특화된 그래프를 표현하는 데 사용되는 클래스의 인터페이스를 생성합니다. 이 인터페이스는 추상 클래스이며, 서브클래스가 구현해야 하는 속성과 메서드를 정의하되 이러한 컴포넌트를 구현하는 방식은 지정하지 않습니다.

이 접근 방식을 사용하면 특화된 서브클래스의 내부 작업을 각기 다르게 구현하는 데 필요한 유연성이 제공될 뿐만 아니라 일관된 인터페이스를 사용할 수 있습니다.

이 예제에서는 네임스페이스 폴더에 인터페이스, 파생된 서브클래스, 유틸리티 함수가 포함되어 있습니다.

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

인터페이스 속성 및 메서드

GraphInterface 클래스는 다음과 같은 속성을 지정하며, 서브클래스가 이들을 정의해야 합니다.

  • Primitive — 특화된 그래프를 구현하는 데 사용되는 그래픽스 객체의 핸들입니다. 이 클래스 사용자는 이러한 객체에 직접 액세스할 필요가 없으므로 이 속성은 protected SetAccessGetAccess를 가집니다.

  • AxesHandle — 그래프에 사용되는 좌표축의 핸들입니다. 특화된 graph 객체는 axes 객체 속성을 설정할 수 있습니다. 이 속성은 protected SetAccessGetAccess를 가집니다.

  • DataGraphInterface 클래스의 모든 서브클래스는 데이터를 저장해야 합니다. 데이터 유형은 다양하고 각 서브클래스가 저장 메커니즘을 정의합니다. 서브클래스 사용자가 데이터 값을 변경할 수 있으므로 이 속성은 퍼블릭 액세스 권한을 가집니다.

GraphInterface 클래스는 서브클래스가 구현해야 하는 세 가지 추상 메서드의 이름을 지정합니다. GraphInterface 클래스의 주석에는 각 서브클래스 생성자가 모든 클래스 속성에 대한 속성 이름/속성값 쌍과 플롯 데이터를 받아야 한다는 권장 사항도 제공됩니다.

  • 서브클래스 생성자 — 데이터 및 P/V(속성 이름/속성값) 쌍을 받아 객체를 반환합니다.

  • draw — 그리기 프리미티브 객체를 생성하고 서브클래스에서 구현된 그래프 유형에 따라 데이터의 그래프를 렌더링하는 데 사용됩니다.

  • zoom — 좌표축의 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가 보호된 클래스 데이터(axes 핸들)에 액세스할 수 있습니다. 객체의 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 클래스에 정의된 인터페이스를 구현하고 두 개의 추가 속성, 즉 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 메서드 구현하기

LineGraphdraw 메서드는 속성값을 사용하여 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 메서드는 camzoom 함수를 사용하는 것을 권장하는 GraphInterface 클래스의 주석을 따릅니다. camzoom은 확대/축소할 수 있는 편리한 인터페이스를 제공하고 addButtons 메서드로 생성된 누름 버튼을 통해 올바르게 작동합니다.

속성 set 메서드 정의하기

속성 set 메서드를 사용하면 속성값이 생성자에서 처음으로 변경될 때 자동으로 코드를 간편히 실행할 수 있습니다. 속성 get 및 set 메서드 항목을 참조하십시오. linegraph 클래스는 속성값이 변경될 때마다 set 메서드를 사용하여 line 프리미티브 데이터를 업데이트하며, 이 경우 플롯이 다시 그려지게 됩니다. 속성 set 메서드를 사용하면 draw 메서드를 호출할 필요 없이 데이터 플롯을 신속하게 업데이트할 수 있습니다. draw 메서드는 모든 값을 현재 속성값에 일치하도록 재설정하여 플롯을 업데이트합니다.

LineColor, LineType, Data, 이 세 개 속성이 set 메서드를 사용합니다. LineColorLineTypeLineGraph 클래스로 추가된 속성이고 이 클래스에 사용된 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 In 버튼을 클릭하면 버튼에 대한 콜백을 제공하는 zoom 메서드가 표시됩니다.

Window showing randomly drawn line segments

속성을 변경하면 그래프가 업데이트됩니다.

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

이제, Zoom Out을 클릭하고 새 결과를 봅니다.

Window showing randomly drawn line segments

관련 항목