构建通用物流系统

时间:2017-05-22 08:32:32

标签: c#

这适用于城市建设者游戏,但该框架应该对任何请求和发送都有用。 基本上,您有供应商和请求者。当请求者在其需要的情况下运行不足时,会提出请求。然后每个滴答所有请求都被处理,适当的供应商将他们的东西发送给相应的请求者,每个人都很高兴。 我正在做的是尝试用泛型完成所有工作。我对泛型非常好但是暂时没有使用它们所以解决方案可能很简单。 这是一些代码:

public interface Requester<T> where T : Item
{
    bool NeedsToRequest();
    Request<T> LodgeRequest();
}

public interface Supplier<T> where T : Item
{
    void Supply(Request<T> request);
}

public class Request<T> where T : Item
{
    public Pile<T> Target;
    public int Amount;
}

所有这些都集中在一个大的中介类中,该类处理请求制作和实现等等。

public abstract class ProductionHandler
{ 
    public List<Supplier<Item>> Suppliers;

    public List<Request<Item>> Requests;

    public abstract bool TryRequest<T>(Request<T> request) where T : Item

}

也是相关的:

public class Pile<T> : List<T> where T : Item
{

    public int Amount
    {
        get { return this.Count; }
    }

}

最大的问题是按T类型对供应商进行排序。我想查看请求列表,获取其类型,从列表中找到匹配类型的供应商,并处理请求。 另一个问题是让我们说你有“花:项目”和“玫瑰:花”和“向日葵:花”。如果某些东西请求鲜花你想要发送它任何类型,所以在这种情况下只比较类型不会那么容易。 如果有人可以将我链接到一篇好文章或建议我如何使一切结构化,那将是非常感谢。

2 个答案:

答案 0 :(得分:2)

可以帮助进一步发展的第一步是使用方差,例如Supplier<Rose>可以投放到Supplier<Flower>Supplier<Item>(反之亦然)。但要实现这一点,供应商必须是一个界面(ISupplier<out T>)。更多信息:Covariance and Contravariance in Generics

这样,您可以保留ISupplier<Item>的列表,但请确保添加了强列表的供应商。意味着列表仅包含ISupplier<Rose>ISupplier<Sunflower>等项目的直接实现 拥有可转换的集合,您只需使用OfType<ISupplier<ItemType>>即可获得所需的供应商。

要耦合请求,有几种可能性(例如,在ISupplier上有两种类型,或者让接口公开另一种类型的通用方法,让供应商按照添加的类型进行检查),但是最干净的方法是使用请求(IRequest<in T>)的逆变接口,可以在接口定义中使用。

示例设置:

public interface ISupplier<out T> 
{
    int Supply(IRequest<T> request);
}

public interface IRequest<in T> //request is input and is contravariant so it can be used in the ISupplier definition. Roughly said it means that if a specific type is used, it can also be used as input for any child type
{
    int Amount{get;set;}
}

public abstract class ProductionHandlerBase
{ 
    public List<ISupplier<Item>> Suppliers = new List<ISupplier<Item>>(); //ISupplier is covariant so any more derrived type (than Item) is still valid 

    public IEnumerable<ISupplier<T>> GetSuppliers<T>() //helper function to make calls easier, but Suppliers could be used directly as well
    {
        return Suppliers.OfType<ISupplier<T>>();
    }

    public bool TryRequest<T>(IRequest<T> Request)
        where T:Item
    {
        //example implementation. (neither optimized or necessariy logical, but it does show the expected types are used)
        foreach(var sup in GetSuppliers<T>())
        {
            if(sup.Supply(Request) > 0 && Request.Amount == 0)
                return true;
        }

        return false;
    }

    public void TestOutput()
    {
        Console.WriteLine(string.Join(", ", Suppliers));
    }
}

class ProductionHandlerExample:ProductionHandlerBase{}


public class Request<T>:IRequest<T>{
    public int Amount{get;set;}
}

public abstract class Supplier<T>:ISupplier<T>{
    int pileAmount = 4; //just to have an example
    public int Supply(IRequest<T> request) //example implementation
    {
        int cnt = Math.Min(request.Amount, pileAmount);
        if(cnt==0)return 0;
        pileAmount -= cnt;
        request.Amount -=cnt;
        return cnt;
    }

    public override string ToString() {return $"{typeof(T).Name}(s): {pileAmount}"; }
}

//items
public abstract class Item{}
public abstract class Flower:Item{}
public class Rose:Flower{}
public class SunFlower:Flower{}
public class Car:Item{}

//suppliers
public class Florist:Supplier<Flower>{} //implementation can still be on a class, as long as the collections are based on the covariant interface
public class Rosy:Supplier<Rose>{}
public class CarDealer:Supplier<Car>{}

使用示例:

var p = new ProductionHandlerExample();
p.Suppliers.Add(new Florist());
p.Suppliers.Add(new Rosy());
p.Suppliers.Add(new CarDealer());

//p.GetSuppliers<Flower>() -> Florist, Rosy
//p.GetSuppliers<Rose>() -> Rosy
//p.GetSuppliers<Item>() -> all items  (Florist, Rosy, CarDealer)

//in the example setup, each supplier has a pile of 4
p.TestOutput(); //Flower(s): 4, Rose(s): 4, Car(s): 4
p.TryRequest(new Request<Rose>{Amount = 2}); //returns true 
p.TestOutput(); //Flower(s): 4, Rose(s): 2, Car(s): 4
p.TryRequest(new Request<Car>{Amount = 5}); //returns false, there are only 4 cars
p.TestOutput(); //Flower(s): 4, Rose(s): 2, Car(s): 0
p.TryRequest(new Request<Flower>{Amount = 5}); //returns true, stock of both florist and Roses are used
p.TestOutput(); //Flower(s): 0, Rose(s): 1, Car(s): 0

在此示例实现中,不进行子类型检查。例如如果花店里的鲜花可能是玫瑰。那是因为这个例子假设花的类型实际上并不知道。如果已知,最好让一般(I)Supplier使用通用的TryRequest方法,其中每个实现都会检查自己的库存(Pile.OfType<>等)

答案 1 :(得分:0)

我认为这与您的TryRequest<T>(Request<T> request) where T : Item方法有关。在Suppliers实施中,您有T,因此您可以使用反射轻松地浏览var supplier = Suppliers.FirstOrDefault ( sup => sup.GetType() .GenericTypeArguments[0] == typeof(T) );

function sortByMutipleFields(...args: string[]) {
  // takes a list of arguments representing sort fields
  // if preceded by a ! then reverse order (!lastName for example)
  // returns a function that works in Array.sort()
  return (a: any, b: any): number => {
    for (var index = 0; index < args.length; index++) {
      let fieldLeft: string;
      let fieldRight: string;
      if (args[index].startsWith('!')) {
        fieldLeft = b[args[index].slice(1)];
        fieldRight = a[args[index].slice(1)];
      } else {
        fieldLeft = a[args[index]];
        fieldRight = b[args[index]];
      }
      if (fieldLeft > fieldRight) { return 1 };
      if (fieldLeft < fieldRight) { return -1 };
    };
    return 0;
  }
}

正如你所看到的,这不是很漂亮,但我现在没有更好的选择。