web-dev-qa-db-ja.com

ミックスインはRubyとのカップリングを作成しますか?

私が次のアーキテクチャを持っているとしましょう:

module Keyring
    @keyring = Keyring.new
    class Keyring
        def key
        def add_key
        def update_key
        def remove_key
    class KeyStorage
    class Encryptor
        class AesEncryptor
class Connection
    def id
    def user_id
    def protocol
    def name
class FtpConnection
    include Keyring
    def Host
    def port
    def username_encryption_id
    def passsword_encryption_id
    def is_passive
    def username
        # Return the decrypted value given an encryption key id.
        return @keyring.key(self.username_encryption_id)
    def password
        # Return the decrypted value given an encryption key id.
        return @keyring.key(self.password_encryption_id)

ご覧のとおり、FtpConnetionはキーリングを使用しています。

さて、キーリングは(名前で)キーリングモジュールに結合されていませんか?別のプロジェクトでFtpConnectionを使用したい場合は、Keyringを持ってくるか、まさにその名前の別のモジュールを実装する必要がありますよね?これは悪いデザインではありませんか?

3
Chad Johnson

この種の名前の結合を悪い設計と見なす必要がある場合、Railsは非常に悪いソフトウェアであり、継承の概念全体を放棄する必要があります。ソフトウェアはライブラリクラスに依存しており、それらを含めて使用した瞬間から常に一定の名前が付けられます。たとえば、Qtライブラリで何かを書くと、すべてがQCheckBoxなどに「結合」されます。またはRailsを使用する場合、その後、ActiveRecordを使用して回避することができず、それを置き換えたい場合は、クラスとメソッドの名前をまったく同じにして全体を書き直す必要があります。

明確に設計されたインターフェースを備えたモジュールがあります。 FtpConnectionクラスは、Keyringの内部状態について何も知る必要はなく、その内部データにアクセスする必要もありません。独自のファイルにある限り、いつでも同じ名前とインターフェイスを持つ別のモジュールに置き換えることができます。

しかし、モジュール自体の一種のグローバル変数としての@keyring変数の宣言と使用法を再考します。複数のクラスが単一のインスタンスを共有する必要が絶対にない限り、これはFtpConnection内で独立して宣言する必要があります。

編集:

単一のインスタンスが必要な場合は、中央のアプリケーションモジュール(Railsモジュールin Ruby on Rails)など)を作成して、クラスインスタンス変数の形式、行上の何か

module App
  class << self
    def keyring
      @@keyring ||= Keyring.new
    end
  end
end

そしてもちろん、pdrは正しいです、あなたがここに持っているものはミックスインではありません。 Mixinは別の方法で機能します。それらは、それらを含むクラスで定義されたメソッドにアクセスします。ある意味で、多重継承に似たものを実装するのはRuby)の方法なので(しかし、どういうわけかより強力です)、それらは子クラスの機能にアクセスするスーパークラスであると言えます。はカップリングの大きな負荷ですが、前述のように、継承構造内でのカップリングについて心配する必要はありません。この用語は、独立したままであるか、データベースドライバーのように非常に柔軟な置換オプションが必要なプロジェクトのより大きなモジュールに関連していると思います。しかし、そこでも、実際にこの動作を実装するクラスとモジュールの名前に依存しています。

編集:

実際のFtpConnectionクラスの前にファイルの先頭部分でそれを必要とし、App.keyringでアクセスします。

Requireとincludeを混同しないように注意してください。 Requireは、モジュールが1回だけロードされることを確認します。インクルードは、使用するたびにロードします。これは主にミックスインで使用されます(ただし、pdrが言ったように、ここには実際にはミックスインがありません)

require 'app.rb'

class FtpConnection
  def username
    return App.keyring.key(self.username_encryption_id)
  end
end

編集:

はい、どういうわけかそうです。ただし、ライブラリ、アプリ、クラスなどがハッシュを使用している場合は、ハッシュクラスの実装に結合されます。または同様の何か、それは少なくとも同じ名前とインターフェースを持つ何かを実装する必要があります。それがモジュラーソフトウェアの仕組みです。プロジェクトを続行する場合は、RubyのNetモジュールを使用してftp機能を実装する可能性があります。だからあなたは多かれ少なかれ次のような行を持つことを余儀なくされています

require 'net/ftp'

その瞬間から、このモジュールまたは同様のモジュールを利用できるようになります。 FtpConnectionを使用するアプリを作成する場合、同じことが当てはまります。追いつめられて。とにかく、実際にはもっと抽象化レイヤーしか持てませんが、そのような場合は役に立ちません。

必要に応じて、同じパターンを繰り返すことができます。 FtpConnection自体を再利用する場合は、Appモジュールが必要です。もちろん同じトリックを行うこともできますが、キーリングモジュール自体にキーリングを保持します。

または、キーリングを必要とするすべてのクラスを別のモジュールにグループ化します。このようにして、少なくとも1か所で中央アクセスを行うことができます。 FtpConnectionを再利用したいとしますが、今回はKeySomethingという名前の別のキーリングを使用します。

module App
  class << self
    def keyring
      @@keyring ||= KeySomething.new
    end
  end
end

コードを1回変更して終了しました。キーリングが必要とする可能性のあるあらゆる種類の初期化についても同じです。もちろん、新しいキーリングは、コードで使用するすべてのメソッドを実装する必要があります。

1

ここでmixinのポイントを見逃していると思います。

Rubyのミックスインは通常、機能を拡張するために任意のクラスに含めることができる(クラスなしの)メソッドをいくつか備えたモジュールです。

たとえば、

module Keyring
    def key
    def add_key
    def update_key
    def remove_key

次に、これをFtpConnectionに含めることができます。これにより、上記で定義した4つのメソッドが得られます。

ミックスインの作成に関する良い記事があります。

ここでやろうとしているのはカプセル化です。このためには、@ keyringフィールドをFtpConnectionクラスに移動し、インクルードを削除する必要があります。

module Keyring
    class Keyring
        def key
        def add_key
        def update_key
        def remove_key
    class KeyStorage
    class Encryptor
        class AesEncryptor
class Connection
    def id
    def user_id
    def protocol
    def name
class FtpConnection
    @keyring = Keyring.new
    def Host
    def port
    def username_encryption_id
    def passsword_encryption_id
    def is_passive
    def username
        # Return the decrypted value given an encryption key id.
        return @keyring.key(self.username_encryption_id)
    def password
        # Return the decrypted value given an encryption key id.
        return @keyring.key(self.password_encryption_id)

これでdoesは、2つのクラス間に緊密な結合を作成するため、おそらくキーリングをコンストラクターに渡す必要があります(依存性注入)。

1
pdr

よく一般的に言えば、はい。ただし、完璧なデザインに固執することに関して熱狂的になりすぎることを心配しないでください(ただし、一般的にはそうしてください)。ちょっとしたコピーパスタをしなければならないなら、それは大丈夫です。わかりません。

0
user32288