実際のプロジェクトでDSL(ドメイン固有言語)を使用する例

DSL(ドメイン固有言語)-任意のドメインの問題を解決するために固有の言語(JavaやC#などの汎用言語とは対照的)。 ウィキペディアにはより詳細な説明と例がありますが、アプリケーション(C#)にBoo言語に基づいて独自のDSLを組み込むのが非常に簡単であることについて書きたいと思います。

Active Directoryから企業情報システムXXX(ユーザー、組織単位、グループなど)へのオンラインデータ同期のためのサービスがありました。 ここでの主な明白な難点は、特定の企業ごとに独自のADスキーム(OrganizationalUnitを使用できるユニット用、グループ用など)と、部門および従業員を表す独自のクラスがあることです。 XXXシステム(そのプロパティとロジック、および同期サービスのレベルでは、それらについては何も知りません、その基本クラスのみ)、さらに同期のさまざまなルール(どこかで投稿を同期する必要がある場合)。

次のように、XMLを介してADをXXXにマッピングするタスクのソリューションが表示されます。


しかし、ここで多くの問題が発生します-独自のルールパーサー(XMLでルールオブジェクトを作成する)とルールインタープリター(それらを満たす)を実装する必要性、制限されたXML機能(型変換の指定や、名前への追加などのやや複雑な計算など)ドメイン名を含むログインプレフィックス)など。 XMLの主な利点は、GUIをそのようなルールに簡単に付加して設定できることです。また、タスクはよく知られており、何度も解決されています。

これらの問題により、タスクはDSLを使用して十分に解決されるタイプのタスクに適合すると考えるようになりました。 この分野での経験と知識はあまりなかったため、最初は少し調査を行い、いくつかのプロトタイプを作成しました。 私たちは主に、BooのDSL-.NETのドメイン固有言語と、本の著者のRhino DSLフレームワークに基づいていました。

この本のDSLの例を次に示します。

承認規則を設定するためのDSL:


モジュラーソフトウェア配信ルールとモジュール要件を設定するためのDSL(+は、Intellisenseを使用したDSLエディターの実装例と、SharpDevelopのコードエディターに基づく構文強調表示です):


これらはすべて、Boo言語でホストされる「内部」DSLの例です。つまり、上記のコードは、通常のclrクラスにコンパイルまたは解釈できる正当なBooコードです。 Booが選ばれた理由は、DSLの構文がかなり便利で、構文を拡張するための豊富な可能性があるためです(Cのマクロなどのマクロを記述できますが、テキストではなく構文ツリーで操作できます)+開かれており、便利なコンパイラAPIがありますアクションを簡単に埋め込むことができます。

特に便利なのは、Booが通常の.Netクラスを出力として提供するという事実です。 その結果、例1のDSLファイルを処理した後、Evaluate(user)などのメソッドを持つRuleタイプを取得します。呼び出されると、Booコードが実行され、ユーザーに認証が必要かどうかの結果が表示されます。 つまり、開発者とアプリケーションの観点から見ると、DSLで記述されたルールが通常のコードのように記述されているかのように、すべてが完全に透過的に見えます。 とてもかっこいい。

結果


以下は、部門の階層も考慮に入れながら、ユーザーとOrganizationalUnitをADから企業システムXXXの部門と従業員に同期するかなり複雑なルールの例です。


ところで、これらのルールは、プラグインをインストールすることなく、スタジオで透過的にデバッグされます。

内部実装


実際、実際、ここではすべてが単純であり、すべてが標準のBoo構文によって行われます。 ルールは、クラスのコンテキストでコンパイルされます。ルールには、rule、for_typeなどの名前を持つメソッドがあります。 だからコード

for_type ユーザー
これは実際には、1つの文字列パラメーター「user」を使用したfor_typeメソッドの呼び出しです。 そして、メソッド内で、渡された値をルールプロパティに書き込むだけで、将来はそれを操作できます。

エンティティとldapは、特別なBooインターフェース-IQuackFooを実装するタイプを持つルールのプロパティです。これにより、動的型付け(IExpandoObjectやC#のdynamicなど)を実装できます。 したがって、ldap.GivenNameがルールに記述されている場合、ldapクラスは実際にユーザーの属性で「GivenName」という名前の属性を探し、その値を返します。 エンティティも同様です。

構文的に扱いにくい唯一の場所は、entity.Sid == ldap.objectGUIDの式です。 これは、コンパイル時に機能し、entity.Sid == ldap.objectGUID式の構文ツリーを別の式に置き換えるメタメソッドです。 具体的には、ここでは、キーが与えられたプロパティの名前をツリーから単純に解析し、それらを覚えています。 メタメソッドは、特別な属性でマークされたC#メソッドのように見え、入力として構文ツリーを取り、新しいツリーを返します。 すべてが簡単です:)。

実装の詳細について質問するか、BooのDSLを読んでRhino DSLを選んでください。すべてが非常によく説明されています。RhinoDSLを使用すると、DSLプロジェクトをBooに数分で接続できます。

Source: https://habr.com/ru/post/J111134/


All Articles