Responsabilità astratta dal chiamante senza introdurre complessità

0

Ho due classi:

public class Child {
    public List<Vector2> localPoints;
    public List<Vector2> localEdges;
}

public class Parent {
    public List<Child> children;
    public Vector2 worldPos;
}

Un genitore ha una posizione nel mondo worldPos . localPoints e localEdges in child contiene punti relativi a worldPos .

Per ottenere la posizione nel mondo di un Vector2 in localPoints o localEdges il chiamante può semplicemente aggiungere worldPos a ciascun elemento.

      foreach (Parent parent in parents) {
        foreach (Child child in parent.children) {
            foreach (Vector2 point in child.localPoints) {
                Debug.Log(point + parent.worldPos);
            }
            foreach (Vector2 edge in child.localEdges) {
                Debug.Log(edge + parent.worldPos);
            }
        }

Il problema è che ho più elenchi e persino forme memorizzate in un bambino. Dare al chiamante la responsabilità di aggiungere worldPos ad ogni utilizzo diventa molto complicato.

In che modo posso astrarre quella compensazione senza introdurre ulteriore complessità computazionale?

Ci ho pensato e ho trovato due soluzioni che annullano la compensazione:

  1. Ogni volta che worldPos cambia, passa attraverso i dati di ogni bambino applica la modifica. localPoints e localEdges ora sarebbero le posizioni attuali nel mondo.

  2. Memorizza worldPos in ogni figlio e usa un getter personalizzato che crea una copia dell'elenco desiderato, applica l'offset a ciascun elemento nell'elenco e lo restituisce.

Sfortunatamente entrambi presentano complessità computazionale.

Come posso astrarre la compensazione senza introdurre complessità computazionale?

    
posta Tagor 27.07.2018 - 03:16
fonte

3 risposte

0

Abstract responsibility from caller without introducing complexity

How can i abstract away that offsetting without introducing additional computational complexity?

L'astrazione è una forma di complessità. Possiamo discutere sul grado di complessità, ma mi aspetto che tutti concordino sul fatto che un'astrazione aggiunge una quantità non nulla di complessità a un codice base.

Tuttavia, sono d'accordo sul fatto che questo debba essere estratto da un chiamante.

How do i abstract away the offsetting without introducing computaional complexity?

Ancora una volta, è impossibile fare qualcosa che non richiede alcuno sforzo. Vorrei riformulare le tue aspettative in modo che tu minimizzi la complessità computazionale piuttosto che evitarlo.

  1. Every time worldPos change, go trough the data in each child apply the change. localPoints andlocalEdges would now be the actual positions in the world.

Questo non è un buon approccio. Stai facendo lo sforzo di convertire tutti i punti, anche se non sai nemmeno se avrai mai bisogno di loro.

Supponiamo che la posizione del mondo cambi, ricalcoli tutto (ad esempio 100 vettori). Sono richiesti solo due valori prima che la posizione del mondo cambi di nuovo e devi ricalcolare tutto di nuovo. Hai effettivamente eseguito 98 conversioni inutili.

  1. Store worldPos in each child aswell and use a custom getter that creates a copy of the desired list, apply the offset to each element in the list and return it

Questo è più vicino a quello che vuoi, ma non è buono. Ora stai memorizzando worldPos sia nel genitore che nel bambino. Cosa succede se ti dimentichi di aggiornare uno di loro?

Nella migliore delle ipotesi, hai dati duplicati e sei responsabile della sincronizzazione. Nel peggiore dei casi, gestisci male la sincronizzazione e ti strapperai i capelli cercando di eseguirne il debug.

Una semplice soluzione sarebbe quella di avere il child store come riferimento al suo genitore, dove è in grado di recuperare il valore (non duplicato).

Un'altra soluzione sarebbe quella di richiedere sempre i valori tramite il genitore , non tramite il bambino. La distinzione qui è contestuale. A volte è meglio accedere a un bambino tramite i suoi genitori. A volte è meglio usare direttamente il bambino e fare in modo che il bambino parli con i suoi genitori quando è necessario.
Suppongo che tu sia attualmente in uno scenario in cui i chiamanti hanno un riferimento al bambino, non al genitore.

Una seconda considerazione è come intendi accedere a questi elenchi. Hai intenzione di iterare su di loro? Stai cercando articoli specifici?

In base al tuo esempio, assumerò che tu stia eseguendo un'iterazione su di essi e, pertanto, non ti dispiacerà convertire l'intera lista quando si accede alla lista.

Avrai bisogno di qualcosa di simile:

public class Child {
    private Parent parent;

    public List<Vector2> LocalPoints;
    public List<Vector2> LocalEdges;

    public Child(parent)
    {
        this.parent = parent;
    }

    public List<Vector2> WorldPoints => LocalPoints.Select(localVector => parent.worldPos + localVector).ToList();
    public List<Vector2> WorldEdges => LocalEdges.Select(localVector => parent.worldPos + localVector).ToList();
}

Questo fa quello che ti serve. Niente che tu non stia effettivamente memorizzando gli elenchi di "mondo". Stai semplicemente calcolandoli al volo, il che significa che non devi tenere traccia delle modifiche a parent.worldPos . Ogni volta che lo calcoli al volo, lavorerai automaticamente con il valore corrente.

Se hai bisogno di ulteriore ottimizzazione delle prestazioni quando accedi a singoli articoli dall'elenco (volendo evitare di convertire gli elementi indesiderati), allora hai bisogno di una soluzione più complessa.

Un'ultima considerazione: il caching. Questa è un'arma a doppio taglio.

  • Se i vettori vengono recuperati molte volte più del worldPos in realtà cambia, puoi ottenere alcuni guadagni in termini di prestazioni calcolando i valori una sola volta quando worldPos cambia.
    • Tuttavia, questo ha un costo enorme: dover gestire la memorizzazione nella cache. Ciò richiede uno sforzo di sviluppo considerevole in cambio di un minore sforzo di prestazioni .
  • Se worldPos cambia più spesso di quanto si ottenga per i vettori, l'approccio "cache all" ridurrà le prestazioni invece di aumentarle.
    • Avresti quindi bisogno di un sistema che memorizza i valori nella cache solo quando vengono richiesti per la prima volta, un altro livello di complessità oltre all'approccio "cache all".

Questo deve essere superato. Poiché la compensazione dei vettori non è un calcolo ingombrante (sta letteralmente facendo x1+x2 e y1+y2 , non guarderei il caching ora.
La complessità di sviluppo dell'implementazione della memorizzazione nella cache non supera la complessità di calcolo di alcune aggiunte di numeri extra.

L'ottimizzazione prematura di solito non è una buona cosa . Prendi nota del problema di prestazioni potenziale ma attendi finché non si presenta un problema di prestazioni effettivo e cerca il collo di bottiglia. Il collo di bottiglia potrebbe trovarsi in una posizione completamente diversa.

    
risposta data 27.07.2018 - 10:44
fonte
0

Generalmente non vuoi astrarre la compensazione.

Hai gli oggetti ben calcolati così com'è.

Potrebbero esserci alcuni schemi di utilizzo in cui sarebbe più efficiente immagazzinare i coordinati del mondo regolati nel bambino invece delle coordinate locali (sono intercambiabili). Ma le aggiunte che stai facendo non sono costose. E la scelta di memorizzare le coordinate locali (globali) funziona meglio se si generalizzano a più livelli di parentela o diversi sistemi di coordinate globali

    
risposta data 27.07.2018 - 04:48
fonte
0

Il tuo modello è scomodo in due modi.

Invece di avere classi separate per Genitore e Figli, sarebbe molto più semplice avere una sola. Padre e figlio non sono classi di oggetti del mondo reale, sono qualifiche che cambiano con la prospettiva dell'osservatore. Quindi sostituiamo entrambi con Thingy.

In secondo luogo, un Thingy potrebbe avere figli, ma nel tuo mondo in cui ogni cosa viene aggiornata e deve essere disegnata di volta in volta, è molto più comodo memorizzare un riferimento al genitore Thingy. Il thingy che non ha genitore è il tuo mondo.

Quindi determinare le posizioni assolute sarà un gioco da ragazzi: Parent.Position + this.Position.

    
risposta data 27.07.2018 - 22:54
fonte

Leggi altre domande sui tag