Sto lavorando a un progetto abbastanza nuovo in cui mi piacerebbe condividere alcuni elementi di configurazione usati da un plugin JavaScript. Nello specifico, il progetto utilizza jQuery dataTables e voglio trovare un buon modo per condividere gli elementi di configurazione.
Al momento, il codice che ho ricevuto è abbastanza semplice. È approssimativamente equivalente a:
// It might not be super-essential to read this
function TableSuperClass(tableElement, moreOptions) {
var options = {
pagination: true, // turn on pagination
iDisplayLength : 50, // pages are fifty rows long
lengthChange: false, // stop users changing length of pages
searching : false // disables searching
};
jQuery.extend(options, moreOptions); // merge moreOptions into options
jQuery(tableElement).DataTable(options); // we create a table with the extended options
// This is a super-handy method we'd like to use everywhere
this.repopulate = function usefulRepopulateFunction() {...};
}
function ObjectOwningTable() {
// this object does other things too
this.table = TableSuperClass({
// various column mappings for a particular table type and
// what to order the table on
columns : [...],
// implements row highlighting, partly based on column mappings
fnRowCallback : function() {...}
});
}
function AnotherObjectOwningTable() {
// this object does other things too
this.table = TableSuperClass({
// various column mappings for a different type of table, switched
// based on some logic inside this class
columns : someTest ? [...] : [...],
// implements ordering a different way
aDesc : [...] // aDesc is a legacy API call that lets you specify the col to order on
});
}
Il problema è che ora ho un altro tipo di tabella. È utilizzato in una vista di stampa. Ha gli stessi mapping di colonne di ObjectOwningTable, ha bisogno del superfluo ripopolamento e disabilita la ricerca da TableSuperClass, ma non ha l'impaginazione, poiché una stampa deve mostrare l'intero set di dati.
Posso pensare a due soluzioni di base:
- Risolvi il problema con l'ereditarietà . Make TableSuperClass fornisce la comoda interfaccia ripopolata e la ricerca disabilitata, quindi una sottoclasse con paginazione, quindi chiamata in ObjectOwningTable e AnotherObjectOwningTable. Per la mia tabella di stampa, chiamo TableSuperClass con gli stessi mapping di colonne utilizzati in ObjectOwningTable (forse li estro a una costante globale o qualcosa del genere).
- Risolvi il problema con la composizione . Ho un singolo DynamicTable che fornisce solo l'interfaccia ripopolata, quindi passa un elenco di oggetti semplici che vengono applicati alle opzioni in ordine. Questi semplici oggetti vivono tutti su un enum da qualche parte, quindi posso vedere come potrebbero scontrarsi mentre li scrivo e mi permettono di combinare le funzionalità più avanti sulla linea.
Quindi, a livello di consumatore, la prima soluzione sembrerebbe essere
function ObjectOwningTable() {
this.table = PagedTable(FOO_REPORT_COLUMN_MAPPINGS);
}
function AnotherObjectOwningTable() {
this.table = PagedTable(someTest? BAR_COLUMN_MAPPINGS : BAZ_COLUMN_MAPPINGS);
}
function PrintObjectOwningTable() {
this.table = TableSuperClass(FOO_REPORT_COLUMN_MAPPINGS);
}
Mentre il secondo apparirebbe come
function ObjectOwningTable() {
this.table = DynamicTable([
DATA_TABLES_OPTIONS.pagingFixedAtFiftyRows,
DATA_TABLES_OPTIONS.disableSearching,
DATA_TABLES_OPTIONS.fooColumnsAndHighlighting
]);
}
function AnotherObjectOwningTable() {
var columnMappings = someTest ? DATA_TABLES_OPTIONS.barColumnMappings : DATA_TABLES_OPTIONS.bazColumnMappings;
this.table = DynamicTable([
DATA_TABLES_OPTIONS.pagingFixedAtFiftyRows,
DATA_TABLES_OPTIONS.disableSearching,
columnMappings
]);
}
function PrintObjectOwningTable() {
this.table = DynamicTable([
DATA_TABLES_OPTIONS.disableSearching,
DATA_TABLES_OPTIONS.fooColumnsAndHighlighting,
DATA_TABLES_OPTIONS.disableSortingControls
]);
}
Non sono sicuro di cosa scegliere. L'approccio ereditario si sente molto problematico. Sembra molto inflessibile, e posso vedere molti modi per gli sviluppatori futuri di "intrappolare" la funzionalità in particolari tassonomie di classe. Penso anche che le catene di eredità lunghe siano più difficili da ragionare rispetto alle strutture piatte. Ma l'approccio compositivo è un po 'più prolisso, e penso che i miei colleghi potrebbero obiettare alla ripetizione. Eppure penso che il mixaggio e l'abbinamento siano davvero utili.
Esiste una terza opzione, che consiste nel creare una sottoclasse di DynamicTable che associa le opzioni pagingFixedAtFiftyRows e disableSearching e viene quindi chiamata dagli oggetti che la utilizzano, passando nelle loro peculiari mappature di colonne. Ma mi chiedo se questo rende le cose più complicate, usando sia sottoclassi che mixin. Mi chiedo anche se crei la tentazione per altri sviluppatori di aggiungere funzionalità alla sottoclasse quando dovrebbero davvero provare e creare mixin.
Che cosa consiglieresti?