Skip to main content

This is documentation for Caché & Ensemble. See the InterSystems IRIS version of this content.Opens in a new tab

For information on migrating to InterSystems IRISOpens in a new tab, see Why Migrate to InterSystems IRIS?

クラス・プログラミングの基本的な考え方

この章は、クラス・プログラミングの詳しい知識がない人に、この種のプログラミングがどのように機能するのかという観念を紹介することを目的としています。クラス・プログラミングの詳しい知識がある場合は、単にコード・サンプルに目を通すことで Caché におけるクラス・プログラミングがどのようなものか把握しておくと役立ちます。

この章は、以下のセクションで構成されます。

例では ObjectScript を使用していますが、この章の概念は、言語にほとんど依存していません。

オブジェクトとプロパティ

クラス・プログラミングでは、中心となる概念はオブジェクトです。オブジェクトは、1 つのセットとして共に格納され、渡される一連の値のためのコンテナです。オブジェクトは、多くの場合、患者、診断結果、取引などの実社会のエンティティに対応しています。

クラス定義は、多くの場合、指定されたタイプのオブジェクトに対するテンプレートです。クラス定義には、それらのオブジェクトに対する値を格納するためのプロパティがあります。例えば、MyApp.Clinical.PatDiagnosis というクラスがあり、このクラスに DateEnteredByPatientIDDiagnosedByCode、およびその他のプロパティがあるとします。

クラスのインスタンスを作成することで、テンプレートを使用します。これらのインスタンスはオブジェクトです。例えば、ユーザが診断結果をユーザ・インタフェースに入力し、そのデータを保存するとします。基礎となるコードは、以下のロジックを持ちます。

  1. 診断結果テンプレートから新しい診断結果オブジェクトを作成します。

  2. 必要に応じて、そのオブジェクトのプロパティの値を設定します。その値は、必須である場合、既定値がある場合、他の値に基づいて計算される場合、完全にオプションである場合があります。

  3. オブジェクトを保存します。

    この操作によって、データが保存されます。

以下は、ObjectScript の使用例です。

 //create the object
 set diagnosis=##class(MyApp.Clinical.PatDiagnosis).%New()

 //set a couple of properties by using special variables
 set diagnosis.Date=$SYSTEM.SYS.TimeStamp()
 set diagnosis.EnteredBy=$username
 
 //set other properties based on variables set earlier by 
 //the user interface
 set diagnosis.PatientID=patientid
 set diagnosis.DiagnosedBy=clinicianid
 set diagnosis.Code=diagcode
 
 //save the data
 //the next line tries to save the data and returns a status to indicate
 //whether the action was successful
 set status=diagnosis.%Save()
 //always check the returned status
 if $$$ISERR(status) {do $System.Status.DisplayError(status) quit status}

以下の点に注意してください。

  • オブジェクトのプロパティを参照するには、構文 object_variable.property_name を使用します。例えば、diagnosis.DiagnosedBy とします。

  • %New() および %Save() は、MyApp.Clinical.PatDiagnosis クラスのメソッドです。

    次のセクションでは、メソッドのタイプと、ここに示すようなさまざまな方法で呼び出す理由について説明します。

メソッド

メソッドはプロシージャです (多くの場合、Caché は、次の章に示すように他の種類のメソッドもサポートします)。メソッドは互いに呼び出すことができ、プロパティおよびパラメータを参照できます。次の章では、Caché でのルールの詳細について説明します。

クラス言語には、インスタンス・メソッドとクラス・メソッドの 2 種類のメソッドがあります。これらは、異なる目的を持ち、使用法も異なります。

インスタンス・メソッド

インスタンス・メソッドが意味を持つのは、クラスのインスタンスから呼び出された場合のみです。これは、通常、そのインスタンスに、またはそのインスタンスで、何かを実行しているためです。以下はその例です。

 set status=diagnosis.%Save()

例えば、患者を表すクラスを定義しているとします。このクラスで、以下の処理を実行するインスタンス・メソッドを定義するとします。

  • 患者の BMI (ボディマス指数) を計算する

  • 患者の情報をまとめたレポートを印刷する

  • 患者が特定の処置の対象となるかどうかを判別する

これらの各処理には、患者に対して格納されているデータの知識が必要であり、それが、多くのプログラマがそれらをインスタンス・メソッドとして作成する理由です。インスタンス・メソッドの実装は、通常、内部でそのインスタンスのプロパティを参照します。以下は、2 つのプロパティを参照するインスタンス・メソッドの定義の例を示しています。

Method GetBMI() as %Numeric
{
 Set bmi=..WeightKg / (..HeightMeter*2)
 Quit bmi
}

このメソッドを使用するには、アプリケーション・コードに以下のような行を含めます。

 //open the requested patient given an id selected earlier
 set patient=##class(MyApp.Clinical.PatDiagnosis).%OpenId(id)  
 
 //get value to display in BMI Display field 
 set BMIDisplay=patient.GetBMI() 

クラス・メソッド

メソッドのもう 1 つのタイプがクラス・メソッド (他の言語では静的メソッドと呼ぶ) です。このタイプのメソッドを呼び出すには、インスタンスを参照しない構文を使用します。以下はその例です。

 set patient=##class(MyApp.Clinical.PatDiagnosis).%New()

クラス・メソッドを作成する一般的な理由は、以下のとおりです。

  • クラスのインスタンスを作成するアクションを実行する必要がある。

    当然ながら、このアクションはインスタンス・メソッドにすることができません。

  • 複数のインスタンスに作用するアクションを実行する必要がある。

    例えば、患者のグループを、別の一次診療医に再割り当てすることが必要な場合があります。

  • どのインスタンスにも作用しないアクションを実行する必要がある。

    例えば、時刻、ランダムな数、または特定の方法でフォーマットされた文字列を返すメソッドを作成できます。

メソッドと変数の範囲

メソッドは、通常、変数の値を設定します。ほとんどすべてのクラスで、これらの変数はこのメソッド内でのみ使用可能です。例えば、以下のクラスを考えてみます。

Class GORIENT.VariableScopeDemo
{

ClassMethod Add(arg1 As %Numeric, arg2 As %Numeric) As %Numeric
{
    Set ans=arg1+arg2
    Quit ans
}

ClassMethod Demo1()
{
   set x=..Add(1,2)
   write x
}

ClassMethod Demo2()
{
   set x=..Add(2,4)
   write x
}

}

Add() メソッドは、ans という名前の変数を設定し、その変数に含まれている値を返します。

メソッド Demo1() は、メソッド Add() を引数 1 と 2 で呼び出し、その答えを書き込みます。メソッド Demo2() も類似していますが、別のハードコードされた引数を使用します。

メソッド Demo1() または Demo2() が変数 ans を参照しようとしても、その変数はそのコンテキストでは定義されておらず、Caché によってエラーがスローされます。

同様に、Add() は変数 x を参照できません。また、Demo1() 内の変数 x は、Demo2() 内の変数 x とは異なる変数です。

これらの変数の範囲は制限されています。それが、Caché クラスの既定の動作 (および他のクラス言語における通常の動作) であるためです。

クラス定義内では、ほとんどすべての場合に、値を、メソッドに対する引数として含めることで渡すことができます。これは、クラス・プログラミングにおける慣例です。この慣例によって、変数の範囲を決定する作業が簡素化されます。

対照的に、ルーチンを作成する場合は、範囲設定を制御するルールを理解する必要があります。これらについては、"Caché ObjectScript の使用法" で説明します。

クラス定数 (パラメータ)

場合によっては、クラスが定数値に簡単にアクセスできると便利です。Caché クラスでは、そのような値がクラス・パラメータです。他の言語では、代わりにクラス定数という用語を使用します。以下に例を示します。

Parameter MYPARAMETER = "ABC" ;

クラス・パラメータは、コンパイル時に値を取得し、後で変更することはできません。

メソッドはパラメータを参照でき、それがパラメータを定義する理由です。以下はその例です。

 set myval=..#MYPARAMETER * inputvalue

クラス定義とタイプ

以下は、クラス定義の例を示しています。これはクラス定義におけるタイプの説明に使用します。

Class MyClass Extends %Library.Persistent
{
Parameter MYPARAMETER = "ABC" ; 

Property DateOfBirth As %Library.Date; 

Property Home As Sample.Address; 

Method CurrentAge() As %Library.Integer 
{
 //details
}

ClassMethod Addition(x As %Library.Integer, y As %Library.Integer) As %Library.Integer
{
 //details
}

}

このクラス定義は 1 つのパラメータ (MYPARAMETER)、2 つのプロパティ (DateOfBirthHome)、1 つのインスタンス・メソッド (CurrentAge())、および 1 つのクラス・メソッド (Addition()) を定義します。

クラス・プログラミングでは、タイプは、以下の主要な場所で指定できます。

  • クラス自体に対して。Extends の後の要素がタイプです。

    各タイプは、クラスの名前です。

  • パラメータに対して。この場合と残りの場合は、As の後の要素がタイプです。

  • プロパティに対して。Home プロパティの場合、タイプはそれ自体がプロパティを含むクラスです。

    この場合、タイプはオブジェクト値を持ちます。この例では、これはオブジェクト値プロパティです。

    オブジェクト値プロパティは、他のオブジェクト値プロパティを含むことができます。

  • メソッドの返り値に対して。

  • メソッドによって使用される引数の値に対して。

継承

大部分のクラスベースの言語の主な特徴は、継承です。1 つのクラスは、他のクラスを継承でき、したがって、他のクラスのパラメータ、プロパティ、メソッド、および他の要素を取得できます。このパラメータ、プロパティ、メソッド、および他の要素は、総称してクラス・メンバと呼ばれます。

用語と基本事項

クラス A がクラス B から継承する場合、次の用語を使用します。

  • クラス A はクラス B のサブクラスです。または、クラス A はクラス B を拡張したものです。

    場合によっては、クラス A はクラス B のサブタイプであるともいいます。

  • クラス B はクラス A のスーパークラスです。

    場合によっては、クラス A はクラス B の子クラスであり、クラス B は親クラスであるともいいます。この用語は、一般的ですが誤解を与えることもあります。用語が、SQL テーブルを説明するときにまったく異なる意味で使用されるためです。

クラスが他のクラスから継承する場合、それらの他のクラスのクラス・メンバを、そのスーパークラス自体が継承したメンバも含めて取得します。サブクラスは、継承したクラス・メンバをオーバーライドできます。

1 つのクラスの複数のスーパークラスで、同じ名前のメソッド、同じ名前のプロパティなどが定義されていることがあります。したがって、どのスーパークラスの定義をサブクラスで使用するのかを決定する規則が必要です (次の章の “Caché における継承規則” を参照してください)。

Caché クラス・ライブラリでは、スーパークラスは、通常、それぞれ用途が異なり、メンバは異なる名前を持っているため、メンバ名の競合はほとんどありません。

以下に、Ensemble の例を示します。

/// Finds files in a FilePath directory and submits all that match a FileSpec wildcard to 
/// an associated BusinessService for processing within Ensemble 
Class EnsLib.File.InboundAdapter Extends (Ens.InboundAdapter, EnsLib.File.Common) 

この例は、クラスが、どのようにしてさまざまなスーパークラスからのロジックを結合できるのかを例示することのみを目的としています。この EnsLib.File.InboundAdapterOpens in a new tab クラスは、まったく異なることを実行する以下の 2 つのクラスから継承します。

EnsLib.File.InboundAdapterOpens in a new tab では、メソッドは、これらのクラスの両方およびそれらのスーパークラスからのロジックを使用します。

継承されるクラス・メンバの使用

編集ツールでクラスの定義を表示しても、それに含まれる継承されたメンバは表示されませんが、コードはそれらを参照できます。

例えば、クラス A が 2 つのプロパティを持ち、それぞれが以下のように既定値を持つとします。

Class Demo.A  
{
Property Prop1 as %Library.String [InitialExpression = "ABC"];

Property Prop2 as %Library.String [InitialExpression = "DEF"];
}

クラス B は、以下のように表示されます。

Class Demo.B Extends Demo.A  
{
Method PrintIt()
{
 Write ..Prop1,! 
 Write ..Prop2,! 
}

}

前述したとおり、サブクラスは、継承したクラス・メンバをオーバーライドできます。例えば、クラス C もクラス A から継承できますが、そのプロパティの 1 つの既定値を以下のようにオーバーライドできます。

Class Demo.C Extends Demo.A  
{
Property Prop2 as %Library.String [InitialExpression = "GHI"];

}

サブクラスの使用

クラス B が クラス A から継承している場合、クラス A のインスタンスを使用できる場所であればどこでもクラス B のインスタンスを使用できます。

例えば、以下のようなユーティリティ・メソッドがあるとします。

ClassMethod PersonReport(person as MyApp.Person) {
 //print a report that uses properties of the instance
}

MyApp.Person のインスタンスをこのメソッドへの入力として使用できます。また、MyApp.Person のどのサブクラスのインスタンスも使用できます。以下はその例です。

 //id variable is set earlier in this program 
 set employee=##class(MyApp.Employee).%OpenId(id)
 do ##class(Util.Utils).PersonReport(employee)

同様に、メソッドの返り値 (それが値を返す場合) は、指定されたタイプのサブクラスのインスタンスにすることができます。例えば、MyApp.EmployeeMyApp.Patient がどちらも MyApp.Person のサブクラスであるとします。以下のようにメソッドを定義できます。

ClassMethod ReturnRandomPerson() as MyApp.Person
{
 Set randomnumber = $RANDOM(10)
 If randomnumber > 5 {
     set person=##class(MyApp.Employee).%New()
 }
 else {
     set person=##class(MyApp.Patient).%New()
 }
 quit person
}

メソッドのコンテナとしてのクラス

前述のとおり、クラス定義は、多くの場合、オブジェクトのテンプレートです。また、クラスは、同類の一連のクラス・メソッドのコンテナとなることもあります。この場合、このクラスのインスタンスを作成することはありません。その中のクラス・メソッドを呼び出すのみです。

例については、Caché %SYSTEM パッケージのクラスを参照してください。これについては、前述の “$SYSTEM 特殊変数” の章で紹介しています。

抽象クラス

また、抽象クラスを定義すると役立ちます。抽象クラスは、通常、汎用インタフェースを定義するものであり、インスタンス化することはできません。このクラス内のメソッド定義は、メソッドのシグニチャを宣言しますが、その実装は宣言しません。

開発者は抽象クラスを定義し、インタフェースを記述します。次に、その開発者または他の開発者がサブクラスを作成し、そのサブクラス内でメソッドを実装します。その実装は、抽象クラスで指定されているシグニチャと一致している必要があります。このシステムによって、わずかに用途が異なるが同一のインタフェースを持つ複数の類似したクラスを開発できます。このため、システム・クラスの多くは共通のインタフェースを持っています。例については、“Caché オブジェクト” の章で紹介されているストリーム・クラスとコレクション・クラスを参照してください。

クラスが抽象でない場合でもメソッドを抽象として指定することもできます。

FeedbackOpens in a new tab