Sto sviluppando una libreria PHP per lavorare con grandi numeri (in questo momento solo con una classe "Decimal", ma voglio aggiungere nuove classi, per gestire numeri interi, razionali e complessi). In ogni caso, il problema è il seguente:
Da un lato, PHP non ha sovraccarico di metodo. Posso usare "metodi magici", ma voglio mantenere il suggerimento del tipo nelle firme dei metodi . Anche se ho un'interfaccia comune, ogni tipo di numero merita una gestione specifica perché i suoi interni saranno completamente diversi, quindi finalmente appare come un'idea naturale per aggiungere metodi specifici per tipo per consentire l'interoperabilità tra tali classi ( Decimal, Integer, Rational ...).
D'altra parte, ci sono virtualmente infinite funzioni che qualcuno potrebbe voler applicare a un numero. Attualmente sto implementando molte di queste funzioni in ogni classe per gestire direttamente le proprietà interne ed evitare la creazione e la distruzione di oggetti intermedi (un buon esempio è il sin, exp o cos computing, perché hanno molti calcoli intermedi) .
Quindi, mantenere metodi specifici del tipo aumenta la complessità della classe e mantenere i metodi delle funzioni nelle classi numeriche aumenta anche la complessità della classe.
Il problema è che non ho alcuna idea su come estrarre gli algoritmi di funzione al di fuori delle "classi numero" senza perdere molte prestazioni e senza violare l'incapsulamento delle proprietà.
Qualcuno conosce un modello "buono" da seguire?
Importante da notare: penso che il problema non sia banale soprattutto perché i numeri non sono gestiti in modo gerarchico, ci sono molti sovrapposizioni tra i set di numeri e l'uso delle interfacce non è abbastanza (vedi questo domanda su" self-types " per capire perché lo dico). Non voglio una soluzione "perfetta", ma abbastanza buona da permettermi di progettare una buona libreria per scopi teorici, non solo per il calcolo numerico.
Il caso: immagina una classe che rappresenta un numero, chiamalo Number
. Supponiamo che, oltre alle operazioni aritmetiche di base, si abbiano molte funzioni a una variabile: sin, cos, tan, arcsin, arcocos, arctan, exp, logaritmo, radice quadrata, gamma, riesz, la funzione di airy e così via. .. e aggiungere ad esso molte altre funzioni a due variabili, e forse anche funzioni più complicate (con applicazioni teoriche, non definite a caso) con 3 o più variabili.
Una possibilità è estrarre gli algoritmi per calcolare tali funzioni in altre classi al fine di mantenere sotto controllo la dimensione della classe. Il problema di questo approccio è che quindi devi esporre le classi numeriche interne se vuoi prestazioni, o perdere un sacco di prestazioni se vuoi rispettare SOLID.
Ho tre punti che voglio controllare: dimensioni classi (per migliorare la leggibilità), principi SOLID (perché la libreria deve crescere molto e garantire la qualità e testabilità), prestazioni di calcolo (perché lo scopo della biblioteca è quello di essere utile, non un giocattolo).
Idea proposta:
Finalmente sono arrivato alla conclusione che dovrei astrarre le funzioni usando classi specifiche (e instanziabili). Questo mi permetterà di implementare nuove caratteristiche interessanti come la composizione di funzioni, funzioni di secondo ordine, limiti di calcolo, integrali e differenziazioni in modo simbolico ... Questo è un sacco di lavoro, ma in primo luogo implementerò solo i pilastri per renderlo possibile.
Le funzioni funzioneranno al primo posto direttamente con gli oggetti numerici, e dove vale la pena aggiungerò una rappresentazione interna personalizzata per migliorare le prestazioni. La traduzione dell'oggetto alla rappresentazione interna della funzione aggiungerà un sovraccarico, ma meno della creazione e distruzione di istanze intermedie di Number (o penso di sì, analizzerò questo tipo di modifiche per garantire che l'aumento della complessità non sia uno spreco di tempo e righe di codice ).
Poiché PHP non ha classi parametriche, parte del contratto verrà controllata in fase di runtime con metodi specifici, se possibile in "tempo di costruzione della funzione" piuttosto che in "tempo di valutazione della funzione". Ad esempio, sto pensando di aggiungere due getter per ottenere il tipo di ritorno e l'argomento "tipi" ¹ e n-arity. Per quanto riguarda il secondo getter menzionato, questo sarà solo per "casi d'angolo", poiché preferisco sottoclasse e aggiungere un metodo specifico e accennato.
- Qui uso i tipi di parole, ma mi riferisco ai set di numeri, non è lo stesso anche se sembra così. L'aggiunta di classi di funzioni e istanze mi consente di creare metodi specifici per tipo senza dovermi preoccupare delle dimensioni della classe (il numero di metodi sarà piccolo anche se ho metodi specifici per tipo).