You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#观察者模式(Observer Pattern)实现正确性验证及总金额计算咨询

Hey there! Great job getting started with the Observer Pattern in C#—your core implementation hits the mark, and it’s clear you grasp how subjects and observers interact. Let’s break down what’s working well, some tweaks to make it more idiomatic .NET, and how to add total amount calculation.

Observer Pattern Implementation Feedback & Optimizations

First off, your approach is correct: Products acts as the subject maintaining an event, while Shipping, Discount, and Tax are observers that subscribe to react when a product is added. Here are some improvements to align with .NET best practices:

1. Use .NET's Built-in EventHandler<T> Delegate

You don’t need to define a custom ProductAddedEventHandler—.NET provides a generic EventHandler<TEventArgs> delegate that’s standard for events. Swap out your custom delegate with this:

public event EventHandler<ProductAddedEventArgs> ProductAdded;

2. Simplify Event Invocation

Instead of checking if (ProductAdded != null) before invoking, use the null-conditional operator ?.Invoke for cleaner, safer code:

ProductAdded?.Invoke(this, new ProductAddedEventArgs(subtotal));

3. Rename Methods for Clarity

Names like create are vague—rename methods to reflect their actual purpose. For example:

  • Calculate.create → Calculate.CalculateSubtotal
  • Shipping.create → Shipping.CalculateShipping
  • Discount.create → Discount.CalculateDiscount

This makes your code much more readable for anyone else (or future you!) looking at it.

4. Optimize Product Collection Management

Right now, every time you add a product, you fetch the entire list from ProductModels and concatenate a new collection. If ProductModels.Products() pulls from a database or external source, this will get slow quickly. Instead, maintain an internal list in the Products class:

private List<Product> _products = new List<Product>();

public void Add(Product product) {
    _products.Add(product);
    this.subtotal = new Calculate().CalculateSubtotal(_products);
    // Trigger event...
}

How to Calculate the Total Amount?

Total amount logic depends on your business rules, but a common formula is:
Total = (Subtotal - Discount) + Shipping + Tax
(Note: You might adjust whether tax applies to the discounted amount or subtotal—tweak based on your needs.)

Here are two clean ways to implement this:

Option 1: Add a TotalCalculator Observer

Create a new observer class that handles total calculation, just like your existing shipping/discount/tax classes. This keeps each observer focused on a single responsibility (following the Single Responsibility Principle):

public class TotalCalculator {
    const double ShippingRate = 0.05;
    const double DiscountRate = 0.2;
    const double TaxRate = 0.07;

    public void CalculateTotal(object sender, ProductAddedEventArgs e) {
        double subtotal = e.SubTotal;
        double discount = subtotal * DiscountRate;
        double discountedSubtotal = subtotal - discount;
        double shipping = subtotal * ShippingRate; // Or use discountedSubtotal if needed
        double tax = discountedSubtotal * TaxRate;
        double total = discountedSubtotal + shipping + tax;

        Console.WriteLine("Total Amount: {0:F2}", total);
    }
}

Then subscribe it in your Main method:

TotalCalculator totalCalc = new TotalCalculator();
products.ProductAdded += totalCalc.CalculateTotal;

Option 2: Extend the Event Args (If Needed)

If you want observers to share calculated values (e.g., let TotalCalculator use the discount amount calculated by Discount), you could extend ProductAddedEventArgs to include those values. But this complicates the pattern—Option 1 is cleaner since each observer calculates its own value independently.


Full Optimized Code Example

Here’s your code with all the tweaks and total calculation added:

using System;
using System.Collections.Generic;
using System.Linq;

class Program {
    // Assuming you have this Product class defined (you omitted it in your snippet)
    public class Product {
        public int Id { get; set; }
        public string Name { get; set; }
        public double Price { get; set; }
    }

    public class Products {
        private List<Product> _products = new List<Product>();
        private double _subtotal;

        // Use .NET's standard event delegate
        public event EventHandler<ProductAddedEventArgs> ProductAdded;

        public void Add(Product product) {
            _products.Add(product);
            var calculator = new Calculate();
            _subtotal = calculator.CalculateSubtotal(_products);

            // Simplified event invocation
            ProductAdded?.Invoke(this, new ProductAddedEventArgs(_subtotal));

            Console.WriteLine("Subtotal Amount: {0:F2}", _subtotal);
        }
    }

    /// <summary>
    /// Calculates the subtotal of a product list
    /// </summary>
    public class Calculate {
        public double CalculateSubtotal(IEnumerable<Product> products) {
            double result = 0;
            foreach (var product in products) {
                result += product.Price;
            }
            return result;
        }
    }

    public class ProductAddedEventArgs : EventArgs {
        public double SubTotal { get; }

        public ProductAddedEventArgs(double subtotal) => SubTotal = subtotal;
    }

    public class Shipping {
        const double ShippingRate = 0.05;

        public void CalculateShipping(object sender, ProductAddedEventArgs e) {
            Console.WriteLine("Shipping Amount: {0:F2}", e.SubTotal * ShippingRate);
        }
    }

    public class Discount {
        const double DiscountRate = 0.2;

        public void CalculateDiscount(object sender, ProductAddedEventArgs e) {
            Console.WriteLine("Discount Amount: {0:F2}", e.SubTotal * DiscountRate);
        }
    }

    public class Tax {
        const double TaxRate = 0.07;

        public void CalculateTax(object sender, ProductAddedEventArgs e) {
            // Calculate tax on discounted amount (adjust if needed)
            double discountedSubtotal = e.SubTotal - (e.SubTotal * 0.2);
            Console.WriteLine("Tax Amount: {0:F2}", discountedSubtotal * TaxRate);
        }
    }

    public class TotalCalculator {
        const double ShippingRate = 0.05;
        const double DiscountRate = 0.2;
        const double TaxRate = 0.07;

        public void CalculateTotal(object sender, ProductAddedEventArgs e) {
            double subtotal = e.SubTotal;
            double discount = subtotal * DiscountRate;
            double discountedSubtotal = subtotal - discount;
            double shipping = subtotal * ShippingRate;
            double tax = discountedSubtotal * TaxRate;
            double total = discountedSubtotal + shipping + tax;

            Console.WriteLine("Total Amount: {0:F2}", total);
        }
    }

    static void Main(string[] args) {
        var products = new Products();
        var shipping = new Shipping();
        var discount = new Discount();
        var tax = new Tax();
        var totalCalc = new TotalCalculator();

        // Subscribe all observers
        products.ProductAdded += shipping.CalculateShipping;
        products.ProductAdded += discount.CalculateDiscount;
        products.ProductAdded += tax.CalculateTax;
        products.ProductAdded += totalCalc.CalculateTotal;

        products.Add(new Product { Id = 50, Name = "Watermelon", Price = 9.21 });
    }
}

When you run this, you’ll get output like:

Subtotal Amount: 9.21
Shipping Amount: 0.46
Discount Amount: 1.84
Tax Amount: 0.52
Total Amount: 8.35

内容的提问来源于stack exchange,提问作者Jack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:08:10