La navigazione tra più di una classe è una cattiva pratica? [duplicare]

0

Ho la seguente classe con la seguente funzione (alcuni dettagli di implementazione sono stati saltati):

public class TitleScreen : GameScreen
{ 
    public Texture2D titleScreen;

    public override void Draw (GameTime gameTime)
    {
        ScreenManager.SpriteBatch.Draw (titleScreen, new Rectangle (0, 0, ScreenManager.Game.Window.ClientBounds.Width, ScreenManager.Game.Window.ClientBounds.Height), Color.White);
    }
}

Questa classe è associata a una classe "ScreenManager", che è associata a una partita, che è associata a una finestra. Come puoi vedere, navigo attraverso diverse classi per ottenere i limiti della finestra in modo da poterla disegnare.

La mia domanda è: è una cattiva pratica? Sto accoppiando la classe TitleScreen con Game and Window? Sarebbe meglio implementare una funzione in ScreenManager per restituire la larghezza e l'altezza della finestra?

    
posta David Jiménez Martínez 04.12.2013 - 07:25
fonte

2 risposte

2

Per completare il mio commento sopra:

Il principio guida involontariamente - The Law of Demeter:

Il principio in questione è il Law of Demeter , che afferma molto liberamente (tratto da Wikipedia):

[Objects should] only talk to [their] immediate friends.

In questo senso, il tuo codice sembra violare un principio guida piuttosto importante dell'orientamento agli oggetti. Pertanto, una possibile soluzione sarebbe quella di parlare solo con ScreenManager e chiedergli di darti il ClientBounds . Sfortunatamente, potresti non avere il controllo del codice (cioè ScreenManager , ecc.), Quindi potrebbe essere impossibile. Anche se è possibile, puoi vedere che ScreenManager non può semplicemente chiamare Game.Window.ClientBounds senza rompere la Legge, quindi ha bisogno di chiedere Game per ottenere ClientBounds . A seconda di chi chiedi, questo diventa non mantenibile rapidamente. L'articolo di Wikipedia continua con il seguente avviso:

Although the LoD increases the adaptiveness of a software system, it may also result in having to write many wrapper methods to propagate calls to components; in some cases, this can add noticeable time and space overhead.

Un approccio diverso al principio: come DTO:

Il libro Pulisci codice di Robert Martin, di solito eccellente, chiama il "concatenamento di treni" di cui sopra, sia per il suo aspetto, così come la possibilità che qualcosa vada storto. Tuttavia, viene fatta una distinzione (si spera che allo zio Bob non importi le mie citazioni qui, con alcune modifiche):

Are these two snippets of code violations of the Law of Demeter? Certainly the containing module knows [snip]... That’s a lot of knowledge for one function to know. The calling function knows how to navigate through a lot of different objects. Whether this is a violation of Demeter depends on whether or not [list of objects, in your case: Game and Window] are objects or data structures. If they are objects, then their internal structure should be hidden rather than exposed, and so knowledge of their innards is a clear violation of the Law of Demeter. On the other hand, if [they] are just data structures with no behavior, then they naturally expose their internal structure, and so Demeter does not apply.

Quindi, in sintesi, la Legge di Demetra potrebbe non essere violata, se vediamo gli oggetti intermedi come principalmente DTO (oggetti senza comportamento).

Un possibile suggerimento: IoC

Se stai arrivando al punto in cui questo tipo di cosa diventa un problema, puoi sempre considerare Inversion of Control. In breve, stiamo parlando di eliminare la logica di come ottenere l'oggetto ClientBounds fuori da TitleScreen . In generale quindi, TitleScreen si aspetterebbe di fornire un ClientBounds attorno al tempo della sua costruzione (sebbene ci siano una varietà di altri approcci, come la proprietà o l'iniezione di parametri del metodo); spostando la responsabilità di conoscere la gerarchia dell'oggetto completo da qualche altra parte (in genere un codice di cablaggio).

Sebbene ciò non risolva necessariamente la violazione del LoD, ti dà l'opportunità di rimuovere alcune duplicazioni dal tuo progetto. Rendi anche TitleScreen in meno dipendente dall'intera catena di oggetti e potenzialmente più unità testabile.

È completamente possibile farlo a mano, ma per rendere IoC più realistico e sopportabile, le persone normalmente usano framework di dipendenze per l'iniezione, come Unità o Ninject .

    
risposta data 04.12.2013 - 16:10
fonte
0

Ciò che la schermata del titolo deve sapere è width e height . Da dove proviene questa informazione non dovrebbe interessarsene (accoppiamento libero).

Quando larghezza e altezza sono costanti, le passerei al costruttore.

Quando potrebbero cambiare in alcune circostanze ben definite, vorrei creare un setter per queste variabili ( currentScreen.setSize(width, height) ).

Quando cambiano tutto il tempo, vorrei passare un riferimento all'oggetto ClientBounds ad esso in modo che possa controllarlo ogni volta prima di disegnare una cornice.

Per la logica alla base di questo, potresti leggere il principio di iniezione di dipendenza . Quando un oggetto dipende da un oggetto di un'altra classe, non dovrebbe ottenere quell'oggetto stesso. Dovrebbe riceverlo dall'esterno. Questo rende il tuo codice molto più modulabile e flessibile.

    
risposta data 04.12.2013 - 17:16
fonte

Leggi altre domande sui tag