这张图片激发了我的一些朋友之间的竞赛,他们以更合适的OO风格重写了此代码。



这就是我想出的。
任何想法:

public enum CoffeeKind
{
    Black = 1,
    Cappuccino,
    Espresso
}

public class Coffee
{
    //Measured in celcius
    public int Temprature { get; set; } = 30;
    public bool HasMilk { get; set; }
    public int Sugar { get; set; }
    public CoffeeKind Kind { get; set; }
    public bool Cold => Temprature < 24;
}

public enum CupSize
{
    Small = 1,
    Medium,
    Large,
    ExtraLarge
}

public class Cup
{
    public static Cup SMALL = new Cup(CupSize.Small);
    public static Cup MEDIUM = new Cup(CupSize.Medium);
    public static Cup LARGE = new Cup(CupSize.Large);
    public static Cup EXTRA_LARGE = new Cup(CupSize.ExtraLarge);

    public static Dictionary<CupSize, Cup> Cups = new Dictionary<CupSize, Cup>
    {
        [CupSize.Small] = SMALL,
        [CupSize.Medium] = MEDIUM,
        [CupSize.Large] = LARGE,
        [CupSize.ExtraLarge] = EXTRA_LARGE
    };

    public CupSize Size { get; }
    public int Amount { get; set; }
    public bool Empty => Amount == 0;
    public Coffee Coffee { get; set; }

    private Cup() : this(CupSize.Medium) { }
    private Cup(CupSize size)
    {
        Size = size;
    }

    public void Fill(CoffeeKind kind)
    {
        if (Empty)
        {
            Coffee = new Coffee { Kind = kind };
            Amount = SetAmountBySize(Size);
        }
    }

    private static int SetAmountBySize(CupSize size)
    {
        switch (size)
        {
            case CupSize.Small:
                return 100;
            case CupSize.Large:
                return 300;
            case CupSize.ExtraLarge:
                return 400;
            case CupSize.Medium:
            default:
                return 200;
        }
    }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Name => $"{FirstName} {LastName}";
    public Cup Cup { get; set; }

    public Person() { }
    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    public void GetCoffee(CupSize size, CoffeeKind kind)
    {
        Cup = Cup.Cups[size];
        Cup.Fill(kind);
    }

    public void Drink()
    {
        if (Cup?.Empty == false && Cup?.Coffee?.Cold == false)
        {
            Cup.Amount = 0;
        }
    }

    public void Drink(int amount)
    {
        if (Cup?.Empty == false && Cup?.Coffee?.Cold == false)
        {
            Cup.Amount -= amount;
        }
    }
}

class Program
{
    const int EXIT = -1;
    const int GET_COFFEE = 1;
    const int DRINK_COFFEE = 2;
    const int DRINK_AMOUNT = 3;

    //TODO change these to use values of enum - or just change the calls to them
    const int SMALL = 1;
    const int MEDIUM = 2;
    const int LARGE = 3;
    const int EXTRA_LARGE = 4;

    const int BLACK = 1;
    const int CAPPUCCINO = 2;
    const int ESPRESSO = 3;

    static Person person;
    static void Main(string[] args)
    {
        Console.WriteLine("Enter first name:");
        string firstName = Console.ReadLine();
        Console.WriteLine("Enter last name:");
        string lastName = Console.ReadLine();

        person = new Person(firstName, lastName);

        var choice = GetUserAction();
        while (choice != EXIT)
        {
            if (choice == GET_COFFEE)
            {
                if (person?.Cup?.Empty == false)
                {
                    Console.WriteLine("Old coffee will be thrown out");
                }
                int cupSize = GetCupSize();
                int coffeeKind = GetCoffeKind();

                person.GetCoffee((CupSize)cupSize, (CoffeeKind)coffeeKind);
                Console.WriteLine($"Got a {person.Cup.Size.ToString()} size {person.Cup.Coffee.Kind.ToString()} coffee");
            }
            else if (choice == DRINK_COFFEE)
            {
                if (person?.Cup?.Empty == false)
                {
                    person.Drink();
                }
                else
                {
                    Console.WriteLine("Can not drink without coffee!");
                }
            }

            choice = GetUserAction();
        }
    }

    private static int GetCoffeKind()
    {
        Console.WriteLine($"{BLACK}: Black");
        Console.WriteLine($"{CAPPUCCINO}: Cappuccino");
        Console.WriteLine($"{ESPRESSO}: Espresso");

        var result = int.Parse(Console.ReadLine());

        if (result < BLACK || result > ESPRESSO)
        {
            return GetCoffeKind();
        }

        return result;
    }

    private static int GetCupSize()
    {
        Console.WriteLine($"{SMALL}: {nameof(SMALL)}");
        Console.WriteLine($"{MEDIUM}: Medium");
        Console.WriteLine($"{LARGE}: Large");
        Console.WriteLine($"{EXTRA_LARGE}: Extra Large");
        var result = int.Parse(Console.ReadLine());

        if (result < SMALL || result > EXTRA_LARGE)
        {
            return GetCupSize();
        }

        return result;
    }

    private static int GetUserAction()
    {
        Console.WriteLine("What would you like to do:");
        Console.WriteLine($"{GET_COFFEE}: Get coffee");
        // You can't drink coffee without a cup
        if (person?.Cup != null)
        {
            Console.WriteLine($"{DRINK_COFFEE}: Drink coffee");
            Console.WriteLine($"{DRINK_AMOUNT}: Drink some coffee");
        }
        Console.WriteLine($"{EXIT}: Exit");
        return int.Parse(Console.ReadLine());
    }
}


评论

评论不作进一步讨论;此对话已移至聊天。

在编写此代码时,请确保实现与此处记录的正确的和一致的协议支持相关标准兼容。

令人失望的是,您选择使用C#而不是Java编写此代码。我的意思是,我通常更喜欢C#,但是您应该为该工作选择最合适的工具

#1 楼

您正在滥用null传播运算符。例如

person = new Person(firstName, lastName);

var choice = GetUserAction();
while (choice != EXIT)
{
    if (choice == GET_COFFEE)
    {
        if (person?.Cup?.Empty == false)
        {


人不可能成为null。创建之后,您再也不会将其设置为其他任何东西。有趣的是,我认为这将是C#6的最常用功能,但我几乎从不使用它。您的常数应该是SHOUT_CASE


如果您要为一个枚举成员设置一个显式值,那么我想您应该对它们全部进行以下操作:

public enum CoffeeKind
{
    Black = 1,
    Cappuccino = 2,
    Espresso = 3
}


我认为您的模型在这里有点过时了。浓缩咖啡是黑咖啡,即其中没有牛奶。

public enum DrinkType
{
    FilterCoffee,
    Cappuccino,
    Espresso
}


我认为通常这不是一个枚举。如果我想喝一杯咖啡,该怎么办?我认为您需要可以组成的饮料课。


我在这些领域没有意义:

/>您已经有一个枚举-确保正确实施了平等性,并让人们创建自己的保温杯。


如上所述:

public static Cup SMALL = new Cup(CupSize.Small);
public static Cup MEDIUM = new Cup(CupSize.Medium);
public static Cup LARGE = new Cup(CupSize.Large);
public static Cup EXTRA_LARGE = new Cup(CupSize.ExtraLarge);


如果您想了解杯具,您需要一个橱柜: />
public static Dictionary<CupSize, Cup> Cups = new Dictionary<CupSize, Cup>
{
    [CupSize.Small] = SMALL,
    [CupSize.Medium] = MEDIUM,
    [CupSize.Large] = LARGE,
    [CupSize.ExtraLarge] = EXTRA_LARGE
};



这是一种奇怪的方法:空吗


如果将“数量”设置为0到0,没人会注意到
我经常分心并尝试从空杯子里喝酒(有时甚至是昨天的杯子)

如果未执行该操作,则应该PascalCase。或者,如果您认为自己的人(像我一样)将致力于喝杯中的任何东西,那么就应该始终让他们这样做。是TryDrink

评论


\ $ \ begingroup \ $
“你喜欢冷咖啡”,哈哈。好答案!
\ $ \ endgroup \ $
–方言
16-2-2在17:51

\ $ \ begingroup \ $
@Max-一个IRepository可能有点多,但我认为知道可用的杯子尺寸不是杯子的责任。我会说60°C的咖啡温度得到了这项研究的支持,我无法相信这是真实的。
\ $ \ endgroup \ $
– RobH
16年3月3日在8:36

\ $ \ begingroup \ $
codereview.stackexchange.com/questions/118647/…-我可能会降低温度。杯子应该知道它是什么尺寸,对吗?那么也许带有名称和金额的CupSize结构?
\ $ \ endgroup \ $
–祖伯勋爵
16年3月3日在8:47

\ $ \ begingroup \ $
@Max-我想说一个杯子应该知道它的容量,您可以将其编码为一组预定义的大小,不可变结构的集合听起来像是一个不错的选择。
\ $ \ endgroup \ $
– RobH
16年3月3日,9:37

\ $ \ begingroup \ $
@IsmaelMiguel选择温度的方法太过等待v2;)mug == cup(基于JavaScript相等性)。根据罗马规约,用橡皮鸭喝咖啡是非法的
\ $ \ endgroup \ $
–祖伯勋爵
16年3月3日在13:18

#2 楼

快速说明

int GetCupSize()int GetUserAction()int GetCoffeKind()

使用int.TryParse()会更好,因此任何无效的输入字符都不会引发异常。
int SetAmountBySize(CupSize size)
您实际上并没有在此处设置金额。尝试找到一个更好的名称。
常规
而不是if (someBoolean == false),应使用更惯用的方式,例如if (!someBoolean)

评论


\ $ \ begingroup \ $
SetAmountBySize很好。将前缀从set更改为get。 booleanVar == false是因为我正在使用Elvis运算符,所以它可以为空
\ $ \ endgroup \ $
–祖伯勋爵
16-2-2在15:47



\ $ \ begingroup \ $
你会说!(bool)person?.Cup?.Empty比person?.Cup?.Empty == false吗?如果可以,为什么?
\ $ \ endgroup \ $
–祖伯勋爵
16年2月2日在15:55

\ $ \ begingroup \ $
不,不是真的;-)但是人不会为空(主要是)
\ $ \ endgroup \ $
– Heslacher
16-2-2在15:59



\ $ \ begingroup \ $
感谢您的反馈,我已经修复了所有空检查。
\ $ \ endgroup \ $
–祖伯勋爵
16年2月2日在20:19

#3 楼

我会说杯子里的代码逻辑存在缺陷:)。应该更像以下内容:

Cup coffeeCup = new Cup(size);

while(true)
{
    if(cup.IsEmpty)
    {
        cup.Refill(coffeeType);
    }

    cup.Drink();
}



话虽如此,关于您的代码,我只删除了以下几行:

public static Cup SMALL = new Cup(CupSize.Small);
public static Cup MEDIUM = new Cup(CupSize.Medium);
public static Cup LARGE = new Cup(CupSize.Large);
public static Cup EXTRA_LARGE = new Cup(CupSize.ExtraLarge);


,因为它们并没有增加与IMO相关的任何内容。

评论


\ $ \ begingroup \ $
话虽如此,关于您的代码,我只需要删除以下几行:这些只是方便的方法,可以快速获得如此大小的预制杯子
\ $ \ endgroup \ $
–祖伯勋爵
16年2月2日在20:20

\ $ \ begingroup \ $
@Max是的,我的意思是,它并没有增加太多相关性。我更喜欢使用新的Cup(size)指令或某些工厂方法,例如MakeCup(size)或MakeSmallCup()(或MakeMediumCup()等)。
\ $ \ endgroup \ $
–龙胆K
16年3月3日在7:19

\ $ \ begingroup \ $
感谢您的反馈。我将围绕获得的所有反馈进行讨论,并发布后续帖子(问题)
\ $ \ endgroup \ $
–祖伯勋爵
16-2-3在7:22

#4 楼

我的评论:


OO-这是洒了的杯子。到处都是。除了CupofCoffee,您已经拥有所有东西。标题中的“分配器”只能通过泄漏来推断。

最大:
@radarbob您想详细说明吗?


/>
static void Main(string[] args){
    Person theDeveloper = new Person();
    Dispenser coffeeMaker = new Dispenser();

    CoffeeOrder whatIWant = GetUserAction();
    CupOfCoffee myCuppa = CoffeeMaker.Brew(whatIWant);
    theDeveloper.Drink(myCuppa);
}


面向对象的枚举


结构
封装
控制反转
对域建模。每个OP都有一个分配器和一个咖啡杯。
单一责任原则-是的,它适用于方法。

编写此代码时不会损害任何属性。



艾伦·凯(Alan Kay)解释说:“如果直接设置状态而不使用方法,那是错的。”



OO是分形的-不要仅仅因为以下原因而放弃应用设计:


“代码不多”
”方法很小,我们可以在此处添加更多代码”
看!一类。那是OO!太好了!让代码栏开始。



现实,已中断:



//TODO change these...我们的生产代码库中有700多个。代码就像释放时的快干水泥。

很快就被扔到了一起


就到那儿了。而且我个人认为,这体现为在正式测试中花费2天和3个月的地狱测试失败修复周期之间的差异。 >

评论


\ $ \ begingroup \ $
谢谢。在我当前的代码库中,程序是分配器。但是您的榜样无疑启发了我。我可能只是创建一个单独的项目,以免被我已经写的东西所困扰
\ $ \ endgroup \ $
–祖伯勋爵
16年3月3日在15:49

\ $ \ begingroup \ $
“ OO是分形的-不要仅仅因为以下原因而放弃应用设计”。这只是一次快速的尝试。我收到的所有反馈将帮助我更好地重写它。
\ $ \ endgroup \ $
–祖伯勋爵
16年3月3日在15:51

\ $ \ begingroup \ $
我从问这个愚蠢的小玩具程序中学到了更多有关OO的知识,而不是从我以前尝试过的任何讲座/书中得到的:)
\ $ \ endgroup \ $
–祖伯勋爵
16年3月3日在16:14