Как правильно использовать дженерики для практики программирования на основе интерфейсов для разделения частей системы?C#

Место общения программистов C#
Ответить
Anonymous
 Как правильно использовать дженерики для практики программирования на основе интерфейсов для разделения частей системы?

Сообщение Anonymous »

Я пытаюсь создать гипотетическую систему с ресторанами, продуктами, опциями и идентификаторами продуктов.
У ресторана может быть два типа идентификаторов продуктов: целочисленные и строковые. Поэтому мне нужно это учитывать. Вот почему идентификатор продукта упаковывается для поддержки внешних систем, которые отправляют продукты со строковыми или целочисленными идентификаторами (некоторые интеграции хранят идентификаторы в формате UUID, а некоторые — в целочисленном формате).
У меня есть система доставки, которая получает любой ресторан, реализующий интерфейс (у которого также есть Product, Option и ProductId — все они должны реализовать свои соответствующие интерфейсы).
У меня есть сомнения относительно моего решения, потому что оно слишком много общего кода, который затрудняет написание (представьте, что вам придется написать такой код для более чем 20 ресторанов), а также содержит ошибку. Поэтому я не могу продолжить из-за ошибки.
В настоящее время я не думаю, что необходимо использовать класс адаптера для каждого ресторана (с приведениями внутри) или иметь система доставки, которая использует дженерики в определении класса (поскольку я могу получать рестораны во время выполнения - я не смогу создать систему доставки с соответствующими типами ресторанов - у меня просто нет их во время выполнения без отражения, и мне нужно избегать отражение любой ценой, потому что доставка будет осуществляться во время выполнения (а не запуска), а отражение не оправдано с точки зрения скорости и пропускной способности).
Я думаю, что основная проблема заключается в моем непонимании дженерики и способы их использования в сценариях реальных проектов. Можете ли вы мне помочь?
Вот код (также доступна ссылка на скрипт: https://dotnetfiddle.net/xzi10f):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

public interface IProductId { }

public interface IProductId : IProductId {
public TId GetIdValue();
}

public interface IOption {
public string GetName();
public float GetPrice();
}

public interface IProduct where TProductId : IProductId where TOption : IOption {
public TProductId GetId();

public string GetName();

public float GetPrice();

public void AddOption(TOption option);

public TOption[] GetOptions();
}

public interface IRestaurant
where TProductId : IProductId
where TOption : IOption
where TProduct : IProduct {

public void AddProduct(TProduct product);

public TProduct[] GetProducts();
}

// Real implementation

public abstract class FastfoodRestaurantOption(string name, float price) : IOption {
private readonly string name = name;
private readonly float price = price;

public string GetName() => name;

public float GetPrice() => price;
}

public class BurgerOption(string name, float price) : FastfoodRestaurantOption(name, price) {
private bool fried = false;

public bool Fry() => fried = true;

public bool IsFried() => fried;
}

public class FastfoodRestaurantProductId : IProductId {
public required string Id { private get; init; }

public string GetIdValue() => Id;
}

public class Burger : IProduct {
private float discount = 1;

public float GetPrice()
=> (10 + (GetOptions().Sum(o => o.GetPrice()))) * discount;

public string GetName() => "Burger";

public void ApplyDiscount(float discount) => this.discount = discount;

private readonly List options = [];

public void AddOption(FastfoodRestaurantOption option) {
var burgerOption = (BurgerOption)option;

if (burgerOption.IsFried()) Console.WriteLine($"Burger option named {burgerOption.GetName()} is fried!");

options.Add(burgerOption);
}

public FastfoodRestaurantOption[] GetOptions() => [.. options.Cast()];

public FastfoodRestaurantProductId GetId() {
return new FastfoodRestaurantProductId {
Id = "some_product_id"
};
}
}

public class FastfoodRestaurant
: IRestaurant {

private bool discountDay = false;

public void MakeDiscountDay() => discountDay = true;

private readonly List products = [];

public void AddProduct(IProduct product) {
if (discountDay) {
if (product is Burger burger) {
Console.WriteLine("Product is burger applying discount!");

burger.ApplyDiscount(0.8f);
}
}
products.Add(product);
}

public IProduct[] GetProducts() => products.ToArray();
}

public class ProductShipper {
public void ShipProductsFromRestaurant(
IRestaurant restaurant
) {

foreach (var product in restaurant.GetProducts()) {
var id = product.GetId();

if (id is IProductId stringId) {
Console.WriteLine($"Product ID is STRING: {stringId.GetIdValue()}. Storing in one table...");
} else if (id is IProductId intId) {
Console.WriteLine($"Product ID is INT: {intId.GetIdValue()}. Storing in another table...");
}

Console.WriteLine($"Shipping product named {product.GetName()} with price: {product.GetPrice()}");

foreach (var option in product.GetOptions()) {
Console.WriteLine(
$"Product has option: {option.GetName()}"
);
}
}
}
}

internal static class PlaygroundGenerics {
public static void Main() {
var fastfoodRestaurant = new FastfoodRestaurant();

var burger = new Burger();

var lettuce = new BurgerOption("Lettuce", 10);
lettuce.Fry();

var cheese = new BurgerOption("Cheese", 20);

burger.AddOption(lettuce);
burger.AddOption(cheese);

fastfoodRestaurant.AddProduct(burger);

var shipper = new ProductShipper();

shipper.ShipProductsFromRestaurant(fastfoodRestaurant); // Error here. cannot convert from 'FastfoodRestaurant' to 'IRestaurant'
}
}

public static class Program {
public static int Main() {
PlaygroundGenerics.Main();

return 0;
}
}


Подробнее здесь: https://stackoverflow.com/questions/790 ... ng-for-dec
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»