Come calcolare il prezzo del prodotto e delle opzioni in base alla selezione e al prezzo dell'opzione utente per opzione

4

Sto creando un servizio per le opzioni di prezzo di un prodotto e per il prezzo del prodotto stesso quando il prodotto ha varie opzioni.

Esempio

L'utente seleziona alcune opzioni X tra le opzioni Y disponibili, dove X < = Y. Il servizio calcola quindi il prezzo dalle opzioni selezionate utilizzando il costo per tali opzioni.

Usando lo pseudocodice, va qualcosa del genere:

$totalPrice = 0;

//number of modules comes from user input OR can be derived from it
//price per module comes from database
$productPrice = [PRICE FOR A MODULE] * [NUMBER OF MODULES];

$totalPrice += $ProductPrice;

//option 1 selection comes from user input
if ([OPTION ONE IS SELECTED])
     $totalPrice += [PRICE FOR OPTION ONE]

Domanda:

Come posso decidere chi tratta le opzioni e chi si occupa dei prezzi?

Ad esempio, posso inserire selected options in una classe Pricing ma questo complica Pricing , poiché ora la logica aziendale all'interno della classe Pricing deve essere a conoscenza di selected options e come usarli per calcolare il prezzo.

Forse potrei avere una classe solo per le opzioni e un'altra per la sola determinazione dei prezzi, e poi una sorta di terza classe "Combiner" che sa come combinare opzioni e prezzi per ottenere i totali finali. Ma non sarà troppo complicato?

Esiste un modello per questo genere di cose?

Istruzione dei problemi generali

Un problema più generale è -

Dato

  • A) Input utente (ad esempio Opzioni aggiuntive)
  • B) Specifiche del prodotto (ovvero il numero di sottosezioni del prodotto) che possono essere ricavate altrove dall'input dell'utente e dal proprio servizio
  • C) Prezzi per le sottosezioni, prezzi per le opzioni

Calcola il prezzo totale del prodotto.

Come posso farlo in OO?

    
posta Dennis 11.02.2016 - 23:12
fonte

2 risposte

1

Il codice è C # pseudocode.

Una parola su MVC

Il modello di dominio non si preoccupa delle trasformazioni alle giunture di MVC. Cioè non ci interessa come i dati arriveranno dall'interfaccia utente alle classi business all'archivio dati (e viceversa). Un buon modello di dominio è il riferimento fisso attorno al quale evolve tutto il codice di trasmissione e MVC.

Riepilogo

Sembra molto? No. Le classi appropriate semplificano la codifica e il codice risultante è semplice. Come? Principio della singola responsabilità e incapsulamento.

  • Grandi idee

    • Struttura

      • Un modello di dominio è l'obiettivo principale
      • Gli oggetti coerenti vengono creati dalle selezioni dell'interfaccia utente
      • Gli oggetti coerenti vengono creati dall'origine dati
    • Raccolte personalizzate

      • Principio di responsabilità singola in gioco, ovvero un luogo in cui inserire funzionalità collettive
    • API prezzi

      • "Prezzo" è calcolato dinamicamente.
        • vendite, promozioni, pacchetti di opzioni
      • "Costo" è stato risolto. MSRP in sostanza.
      • Facile, a prova di idiota per il cliente
      • Nasconde in modo mascherato tutti i dettagli dai client API.
  • Modelli, almeno concettualmente

    • Visitor
      • Un PriceRule sa quali proprietà del prodotto utilizzare.

Strutture dati del dominio

Progetta un modello che abbia senso in modo che sia facile da accedere ed è stabile man mano che l'algoritmo e il codice di costo / prezzo si evolvono.

public class Product {
    var Id;
    var Cost;  // MSRP. Before the kibitzing begins.
    // No Price property. Will dynamically calc.
    // My design decision. You can do otherwise.
    var Options;

    // MSRP. No price adjusting.
    public Decimal GetCost { return this.Cost + this.Options.GetCost(); }

    // The visitor pattern. 
    public void GetPrice ( PricingCollection adjustments) {
        if(adjustments == null) return this.GetCost();

        return adjustments.Apply(this);
    }
}

public class Option {
    var Id;
    var Cost;  // MSRP
    bool Discontinued;  // don't show it on the UI if it is.
}

public class OptionCollection {
    var Options;  // Add() method not shown

    public decimal GetCost() {
        var total = 0;

        foreach (var option in Options)
            total += option.Cost;

        return total;
    }
}

del prodotto-Opzioni

    public ProductOptions : Dictionary<Product, OptionCollection>

Compilato da un archivio dati al momento dell'inizializzazione del programma.

Il set completo di opzioni di prodotto e può essere utilizzato nell'interfaccia utente. Cambiare il data-store ovviamente cambia le opzioni disponibili per l'utente.

La rimozione delle opzioni dopo essere state selezionate per un prodotto è un problema con i requisiti. Penserei che tu riempissi gli ordini di prodotti esistenti con le opzioni ora fuori produzione.

Prezzi

Perhaps I can have a class for just options, and another class for just pricing,

Costruito in un insieme separato di classi. Inseriamo il "modello di prezzo" in Product e il "modello di prezzo" sa come adeguare i costi per arrivare a un prezzo finale e totale.

Ad esempio "Acquista l'opzione X e ottieni l'opzione Y a metà prezzo". Oppure "$ 3, OOO fuori dal nostro camion Tough Texas, solo San Jacinto Day."

Non ho intenzione di discutere "interfaccia" vs "classe astratta". Il codice cliente si occupa dell'API, non della sua implementazione.

public abstract class PriceRule {      
    // Sub classes must implement this method
    // As complex as your sales gimmic demands.
    public abstract Decimal Apply (Product thisProduct);
}

PriceRule deve conoscere la struttura del modello di dominio. Va bene. Questa è l'idea del modello di progettazione dei visitatori.

L'API PriceRule implica (per il modello di dominio) che un OptionCollection possa esistere solo w / in un oggetto Product . Mi piace perché mantiene coerenti le nostre strutture: appartiene sempre a qualche oggetto Product . A meno che i requisiti non dettino altrimenti, naturalmente.

 public class PriceRuleCollection {
    var PriceRules; // Add() method not shown

    public Decimal Apply (Product thisProduct) {
        var total = 0;
        foreach (var rule in PriceRules)
            total = rule.Apply(thisProduct);

        return total;
    }      
  }

and then some kind of a third "Combiner" class that knows how to combine options and pricing to come up with final totals. ... But won't that be too complicated?

Al contrario! Potresti pensarlo troppo banale, ma ha 2 scopi importanti.

  • Dichiara esplicitamente il concetto di "modello di prezzo". Il termine fa parte del business per cui stiamo codificando. Possiamo parlare e codificare nei termini dei nostri clienti.
  • Un posto per aggiungere codice al livello di astrazione del "modello di prezzo". Possiamo manipolare l'ordine dell'applicazione delle regole di prezzo. Possiamo aggiungere nuovi concetti come il finanziamento.

.

public class PricingModel{
    public PricingModel( PriceRuleCollection pricing, Product product, ProductOptions allOptions ) {
        this.Pricing = pricing;
        this.Product = product;
        this.ProductOptions = allOptions;   // might be handy. We everything in one place.
    }

    public Decimal TotalPrice() {
        return this.pricing.Apply(this.product);
    }
  }
    
risposta data 10.09.2016 - 22:24
fonte
0

Personalmente vorrei andare con la tua idea di una terza classe. Per me, questo sembra più coerente con un pattern MVC (Model, View, Controller). Se capisco correttamente, un'opzione (o Modulo) ha una proprietà di prezzo. Questo fa parte del tuo modello . Un utente fa selezioni - questo è qualcosa che accade nella tua vista . Quando arriva il momento di calcolare un totale in base alla selezione di un utente, questo dovrebbe essere il lavoro del tuo controller . In altre parole, il controllore chiede alla vista "cosa ha selezionato l'utente?" e ottiene un elenco di selezioni; il controllore quindi interroga il database / modello e chiede "cosa costa ciascuna di queste opzioni selezionate?" e mappa queste selezioni ai prezzi. Ha quindi tutte le informazioni necessarie per eseguire il calcolo, in cui si chiede "qual è il costo totale di tutte queste opzioni?" - e quindi invia tali informazioni alla vista per essere visualizzate all'utente. Per estendere l'intero caso d'uso fino al completamento, se l'utente interagisce con la vista e accetta di procedere con l'acquisto, il controllore può tornare al modello e farlo creare un'entità ordine e aggiornare il "numero venduto" proprietà su ogni opzione / modulo e qualsiasi altra cosa si possa desiderare di fare come parte della propria logica aziendale.

Di regola, la logica aziendale è presente nel controller. Entità e informazioni sulle entità vivono nel modello. L'input utente grezzo e ciò che viene visualizzato all'utente, appartiene alla vista. Questo non è più complicato: potresti avere più "parti", ma ogni parte ha un ruolo ben definito ed è molto più semplice apportare modifiche a una di queste parti mentre il resto rimane lo stesso. Ad esempio, è possibile modificare il modo in cui tutto viene presentato all'utente quanto desiderato e non è nemmeno necessario guardare / toccare / accedere al modello o ai componenti del controller. Allo stesso modo, se si desidera riscrivere l'intera app, ma mantenere gli stessi dati relativi ai moduli / opzioni disponibili, è sufficiente tenere a mente come sono strutturati i dati durante la progettazione del nuovo controller e visualizzare come desiderato.

All'inizio può sembrare un po 'contro-intuitivo, ma la creazione di più componenti può rendere un'applicazione molto più facile da scrivere, eseguire il debug e modificare in seguito. Sembri già capire questo in modo intuitivo, nel senso che stai pensando a cosa dovrebbe essere responsabile di cosa - ma per fortuna non hai bisogno di prendere tutte queste decisioni per te stesso nel vuoto, dato che esistono già modelli consolidati come "MVC" che rispondono alla maggior parte di queste domande per te.

Una buona regola è quella di mantenere tutte le logiche di business separate da tutto il resto. Un prodotto su una mensola ha bisogno di un cartellino del prezzo, non ha bisogno del proprio registratore di cassa per organizzare una vendita. Crea il modulo che gestisce la tua logica di business in modo indipendente e puoi fare tutto ciò che vuoi con esso: riutilizzalo per vendere prodotti diversi inserendo un nuovo modello o crea un nuovo metodo di interazione, ad es. Chiamate API da un'altra applicazione, invece dell'interfaccia utente dedicata.

    
risposta data 12.02.2016 - 10:59
fonte

Leggi altre domande sui tag