Se questo tipo di accoppiamento di nomi dovesse essere considerato come una cattiva progettazione, allora Rails sarebbe un pessimo software e dovremmo rinunciare a tutta l'idea dell'ereditarietà. Ogni parte del software dipende dalle classi della biblioteca e hanno sempre nomi che sono costanti dal momento in cui li includi e li usi. Diciamo che scrivo qualcosa con la libreria Qt, quindi avrò tutto "accoppiato" a cose come QCheckBox. O se utilizzo Rails, non riesco ad andare in giro usando ActiveRecord e, se mi piacerebbe sostituirlo, dovrei riscrivere il tutto con gli stessi nomi per classi e metodi.
Hai un modulo con un'interfaccia chiaramente progettata. La classe FtpConnection non ha bisogno di sapere nulla sullo stato interno di Portachiavi o accedere a nessuno dei suoi dati interni. Finché si trova in un file a sé stante, può essere sostituito in qualsiasi momento da un altro modulo con lo stesso nome e interfaccia.
Ma riconsidererei la dichiarazione e l'uso della variabile @keyring come una specie di variabile globale nel modulo stesso. Finché non è necessario che diverse classi condividano una singola istanza, questa deve essere dichiarata in modo indipendente all'interno di FtpConnection.
Modifica
Se hai bisogno di una singola istanza, creo probabilmente un modulo Application centrale (come il modulo Rails in Ruby on Rails) che lo tiene come variabile globale sotto forma di una variabile di istanza di classe, qualcosa sulla linea
module App
class << self
def keyring
@@keyring ||= Keyring.new
end
end
end
E il pdr ha ragione, naturalmente, che quello che hai qui non è un mixin. I mixin funzionerebbero in un modo diverso. Accedono ai metodi definiti nella classe che li include. In un certo senso, dal momento che è il modo in cui Ruby implementa qualcosa di simile all'ereditarietà multipla (ma in qualche modo più potente), si può dire che sono una super classe che accede alla funzionalità nelle sue classi figlio, che è un grosso problema di accoppiamento. Ma come detto sopra, non dovresti preoccuparti dell'accoppiamento all'interno delle strutture ereditarie. Penso che questo termine si riferisca più a moduli più grandi di un progetto che dovrebbe rimanere indipendente o richiedere opzioni molto flessibili per la sostituzione come i driver del database. Ma anche lì dipendi ancora dai nomi di classi e moduli che effettivamente implementano questo comportamento.
Modifica
Lo richiederebbe solo nella parte principale del file prima della classe FtpConnection effettiva e quindi l'accesso ad App.keyring.
Fai attenzione a non confondere require e include.
È necessario assicurarsi che il modulo venga caricato una sola volta. Includi lo caricherebbe tutte le volte che lo si utilizza. Questo è principalmente usato con i mixin (ma come detto dal pdr, qui non hai un mixin)
require 'app.rb'
class FtpConnection
def username
return App.keyring.key(self.username_encryption_id)
end
end
Modifica
Sì, in qualche modo è così. Tuttavia, se la tua libreria, app, classe o qualsiasi altra cosa usa un hash, allora è accoppiato a un'implementazione di una classe di hash. O qualcosa di simile, deve almeno implementare qualcosa con lo stesso nome e interfaccia. È così che funziona il software modulare. Se continui con il tuo progetto, molto probabilmente utilizzerai il modulo di Ruby's Net per implementare la funzionalità ftp. quindi sei più o meno costretto ad avere una riga come
require 'net/ftp'
Da quel momento in poi sarai obbligato ad avere questo modulo disponibile o uno simile. Se scrivi un'app che utilizza FtpConnection, lo stesso avverrà o questa classe. Nessuna via d'uscita. Non proprio in ogni caso, puoi avere solo più livelli di astrazione, ma in questo caso è inutile.
A seconda delle tue esigenze puoi ripetere lo stesso schema. Se si riutilizza FtpConnection, è necessario disporre del modulo App. Sebbene tu possa ovviamente fare lo stesso trucco, ma tieni il portachiavi nel modulo Il portachiavi stesso.
Oppure raggruppate tutte le classi che hanno bisogno del portachiavi in un altro modulo. In questo modo potresti avere almeno l'accesso centrale in un unico posto. Supponi di voler riutilizzare FtpConnection, ma questa volta usa un altro portachiavi chiamato KeySomething:
module App
class << self
def keyring
@@keyring ||= KeySomething.new
end
end
end
Finito con un cambio di codice. Lo stesso vale per qualsiasi tipo di inizializzazione che potrebbe richiedere il portachiavi. Anche se ovviamente il nuovo portachiavi deve ancora implementare tutti i metodi che usi nel tuo codice.