Più classi con lo stesso nome, ma spazi dei nomi diversi?


11

Ho incontrato qualche codice (C # se è importante) che ha classi che hanno lo stesso nome, ma differiscono nei loro spazi dei nomi. Tendono tutti a rappresentare la stessa cosa logica, ma spesso sono diverse "viste" dello stesso oggetto. I diversi spazi dei nomi fanno talvolta parte della stessa soluzione e persino della stessa dll.

Ho i miei pensieri, ma non sono riuscito a trovare nulla di definitivo su questa pratica da considerare un uso intelligente degli strumenti disponibili o un modello da evitare.

Questa domanda sulla denominazione dei puffi tocca questo, ma dall'altra parte. Disambiguare i nomi richiederebbe probabilmente un prefisso arbitrario e pervasivo, che quella domanda voleva eliminare.

Quali sono le linee guida attorno a questa pratica?


1
In OOP ci sono molte eccezioni, ma all'inizio considero questo cattivo design, specialmente se tutte le classi condividono lo stesso livello di accessibilità. Quando leggo un nome di classe, in qualsiasi file, voglio sapere in quale spazio dei nomi la classe si trova immediatamente senza dover fare riferimento alle direttive dello spazio dei nomi.
Leopold Asperger,

Esiste un caso valido per avere più oggetti correlati che hanno a che fare con lo stesso stato, ma in genere sono separati dalla funzione. Forse hai un oggetto che adatta un modello per una vista. Un altro potrebbe eseguire un calcolo sull'oggetto. Un altro potrebbe essere un adattatore o una facciata. Così si potrebbe avere FooAdapter, FooViewer, FooCalculator, ecc, ma non certo il nome la stessa cosa. Senza ulteriori informazioni sulle classi specifiche di cui stai parlando, è comunque difficile fornire una risposta.

@JohnGaughan - considera Employeecome esempio. C'è un dipendente che è per altri dipendenti, uno per le risorse umane, uno per l'esposizione esterna. Sono tutti chiamati "impiegato" e hanno tutti una varietà di aiutanti (EmployeeRepository, EmployeeView, ecc.). Sono tutti nella stessa dll e mentre condividono alcune proprietà comuni (nome, titolo), differiscono tutti (numero di telefono, stipendio).
Telastyn,

Avendo lavorato con sistemi che modellano Employeepiù volte, sembra che tu abbia bisogno di un modello con l'unione di tutti gli attributi e includa un attributo typeo department. Altrimenti, c'è inutile duplicazione del codice.

5
Non è questo uno dei motivi per cui gli spazi dei nomi iniziano? Per consentire collisioni di nomi?
Dan Pichelman,

Risposte:


5

Cercherò di dare una risposta contro tale utilizzo, dopo aver visto e lavorato su questo anche in uno dei miei progetti.

Leggibilità del codice

Prima di tutto, considera che la leggibilità del codice è importante e questa pratica è contraria. Qualcuno legge un pezzo di codice e diciamo solo che ha una funzione doSomething(Employee e). Questo non è più leggibile, perché a causa di 10 Employeeclassi diverse esistenti in pacchetti diversi dovrai prima ricorrere alla dichiarazione di importazione per scoprire quale sia il tuo input.

Questa, tuttavia, è una visione di alto livello e spesso abbiamo scontri di nomi apparentemente casuali, che nessuno si preoccupa o addirittura trova, perché il significato può essere derivato dal codice rimanente e in quale pacchetto ci si trova. Quindi si potrebbe anche discutere che, a livello locale, non vi è alcun problema, perché ovviamente se si vede Employeeall'interno di un hrpacchetto è necessario sapere che stiamo parlando di una visione delle risorse umane del dipendente.

Le cose si rompono non appena lasci questi pacchetti. Una volta che lavori in un modulo / pacchetto / etc diverso e hai bisogno di lavorare con un dipendente, stai già sacrificando la leggibilità se non ne qualifichi completamente il tipo. Inoltre, avere 10 Employeeclassi diverse significa che il completamento automatico del tuo IDE non funzionerà più e dovrai scegliere manualmente dal tipo di dipendente.

Duplicazione del codice

A causa della natura stessa di ciascuna di queste classi in relazione tra loro, il tuo codice è destinato a deteriorarsi a causa di molte duplicazioni. Nella maggior parte dei casi, avrai qualcosa come il nome di un dipendente o un numero di identificazione, che ciascuna delle classi deve implementare. Anche se ogni classe aggiunge il proprio tipo di vista, se non condividono i dati dei dipendenti sottostanti, si finirà con enormi masse di codice inutile, ma costoso.

Complessità del codice

Cosa può essere così complesso che potresti chiedere? Dopotutto, ciascuna delle classi può mantenerlo semplice come vuole. Ciò che diventa veramente un problema è come propagare i cambiamenti. In un software ragionevolmente ricco di funzionalità potresti essere in grado di modificare i dati dei dipendenti e vuoi rifletterli ovunque. Di 'che una donna si è appena sposata e devi rinominarla da XaYa causa di ciò. Abbastanza difficile da fare proprio ovunque, ma ancora più difficile quando hai tutte queste classi distinte. A seconda delle altre scelte progettuali, ciò può facilmente significare che ciascuna delle classi deve implementare il proprio tipo di ascoltatore o tale per essere informato delle modifiche - che si traduce sostanzialmente in un fattore applicato al numero di classi che devi affrontare . E ovviamente più duplicazione del codice e minore leggibilità del codice. Fattori come questo possono essere ignorabili nell'analisi della complessità, ma sono sicuramente fastidiosi se applicati alle dimensioni della tua base di codice.

Comunicazione del codice

Oltre ai problemi di cui sopra con la complessità del codice, che sono anche correlati alle scelte di progettazione, si sta anche riducendo la chiarezza di progettazione di livello superiore e si perde la capacità di comunicare correttamente con gli esperti del dominio. Quando si discute di architettura, progetti o requisiti, non si è più liberi di fare dichiarazioni semplici come given an employee, you can do .... Uno sviluppatore non saprà più cosa employeesignifichi davvero a quel punto. Anche se un esperto di dominio lo saprà ovviamente. Noi tutti facciamo. una specie di. Ma in termini di software non è più così facile comunicare.

Come sbarazzarsi del problema

Dopo esserti reso conto di questo e se la tua squadra concorda sul fatto che si tratta di un problema abbastanza grande da affrontare, dovrai trovare un modo per affrontarlo. In genere, non è possibile chiedere al proprio responsabile di concedere all'intero team una settimana libera in modo che possano eliminare la spazzatura. Quindi, in sostanza, dovrai trovare un modo per eliminare parzialmente queste classi, una alla volta. La parte più importante di questo è decidere - con tutto il team - cosa Employeesia veramente. Do Non di integrare tutti i singoli attributi in un dio-dipendente. Pensa più a un dipendente principale e, soprattutto, decidi dove Employeerisiederà quella classe.

Se si eseguono revisioni del codice, è particolarmente facile assicurarsi che il problema non si sviluppi ulteriormente, vale a dire fermare tutti nel loro percorso quando vogliono aggiungerne un altro Employee. Inoltre, assicurarsi che i nuovi sottosistemi aderiscano a quelli concordati Employeee non possano accedere alle versioni precedenti.

A seconda del linguaggio di programmazione, potresti anche voler contrassegnare le classi che verranno eventualmente eliminate con qualcosa di simile @Deprecatedper aiutare il tuo team a rendersi immediatamente conto che stanno lavorando con qualcosa che dovrà essere cambiato.

Per quanto riguarda l'eliminazione delle classi di dipendenti obsolete, puoi decidere per ogni singolo caso come è meglio eliminarlo o semplicemente concordare un modello comune. Puoi apporre il nome della classe e avvolgerlo attorno al vero impiegato, puoi usare i motivi (mi viene in mente decoratore o adattatore), oppure oppure oppure oppure.

Per farla breve: questa "pratica" è tecnicamente valida, ma è piena di costi nascosti che si verificano solo più avanti. Anche se potresti non essere in grado di eliminare immediatamente il problema, puoi iniziare immediatamente con il contenimento dei suoi effetti dannosi.


Avere una classe Core sembra una buona soluzione. Altre classi possono estendersi da esso. Per quanto riguarda l'IDE, il completamento automatico è abbastanza intelligente da sapere quale classe suggerirti per adattarsi al meglio al contesto. Nel complesso, non vedo perché questo sia un grosso problema soprattutto se la base di codice viene utilizzata internamente.
Informato il

1
Non è abbastanza intelligente una volta che sei fuori da un contesto. Considera una classe, che in realtà ha bisogno di entrambe le classi problematiche: il completamento automatico non aiuterà affatto. E non li distinguerà nemmeno dalle altre dieci classi. Ma il vero problema è che quel nuovo membro del team, che non sa nemmeno cosa siano effettivamente tutti quei domini di pacchetti e quale dovrebbe scegliere.
Frank,

Se più classi con lo stesso nome vengono utilizzate nello stesso contesto, è difficile. Non ho visto quel caso. Ho visto più classi con lo stesso nome, ma spesso non vengono utilizzate nello stesso contesto in cui hai menzionato.
Informato il

@randomA - purtroppo, questo caso è abbastanza comune in questa base di codice.
Telastyn,

Aspetti positivi, ma non hai davvero fornito una soluzione al problema principale, solo metodologia una volta che il problema principale è stato risolto ("che cosa è realmente un dipendente"). L'unica "soluzione" ovvia ("integra tutti gli attributi individuali in un dio-impiegato") che hai scartato, giustamente. Supponi di avere DB.Employee, Marshalling.Employee, ViewModels.Employee, GUI.Views.Employee, ecc., Qual è la tua soluzione?
Pablo H,

9

Il Scala Collection Framework ne è un buon esempio. Esistono raccolte mutabili e immutabili, nonché raccolte seriali e parallele (e in futuro anche distribuite). Quindi, ci sono, ad esempio, quattro Maptratti:

scala.collection.Map
scala.collection.immutable.Map
scala.collection.mutable.Map
scala.collection.concurrent.Map

più tre ParMaps:

scala.collecion.parallel.ParMap
scala.collecion.parallel.immutable.ParMap
scala.collecion.parallel.mutable.ParMap

Ancora più interessante:

scala.collection.immutable.Map            extends scala.collection.Map
scala.collection.mutable.Map              extends scala.collection.Map
scala.collection.concurrent.Map           extends scala.collection.mutable.Map

scala.collecion.parallel.ParMap           extends scala.collection.Map
scala.collecion.parallel.immutable.ParMap extends scala.collecion.parallel.ParMap
scala.collecion.parallel.mutable.ParMap   extends scala.collecion.parallel.ParMap

Quindi, ParMapsi ParMapestende si estende Mape Mapsi Mapestende si estende Map.

Ma ammettiamolo: cos'altro li chiameresti? Questo ha perfettamente senso. Ecco a cosa servono gli spazi dei nomi!


3
"Ecco a cosa servono gli spazi dei nomi!" => +1
svidgen

Mi piace questo, ma nel mondo C # /. NET quando ho spostato le classi parallele in uno spazio dei nomi secondario parallelo questo si scontra con la classe helper System.Threading.Parallel, che mi capita di usare pesantemente per ottenere il parallelismo. Non volevo usare lo spazio dei nomi "Concurrent", poiché è già utilizzato per indicare "accesso simultaneo" nelle classi di raccolta. Come compromesso ho optato per lo spazio dei nomi secondari "Parallelized".
redcalx,

2

Questa è una domanda davvero interessante in cui le due risposte essenzialmente opposte sono entrambe corrette. Ecco un esempio reale di questa discussione che stiamo avendo su un mio progetto.

Penso che ci siano finora due considerazioni molto salienti che potrebbero farti desiderare di andare in una direzione o nell'altra, attingendo alle due risposte di @Jorg e @Frank:

  1. Se si prevede una situazione in cui potrebbe essere necessario utilizzare più di una delle classi con lo stesso nome all'interno dello stesso contesto di codice, questo è un motivo per non utilizzare lo stesso nome. Cioè, se devi lavorare con, hr.Employeema allo stesso tempo devi guardare le autorizzazioni via security.Employee, diventerà fastidioso molto velocemente.
  2. Al contrario, se le tue classi con lo stesso nome hanno tutte API uguali o molto simili, allora è un buon esempio di quando dovresti usare lo stesso nome con spazi dei nomi diversi. L'esempio di Scala è proprio questo, in cui tutte le Mapclassi implementano la stessa interfaccia o sono sottoclassi l'una dell'altra. In questo caso l'ambiguità o la "confusione" possono probabilmente essere definite una buona cosa, perché l'utente può giustamente trattare tutte le implementazioni della classe o interfacciarsi allo stesso ... che è il punto di interfacce e sottoclassi.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.