C# 如何创建完美的 OOP 应用程序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9444818/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 07:26:29  来源:igfitidea点击:

How to create the perfect OOP application

c#oop

提问by sunder

Recently I was trying for a company ‘x'. They sent me some set of questions and told me to solve only one.

最近我正在为一家公司“x”尝试。他们给我发了一些问题,并告诉我只解决一个问题。

The problem is like this -

问题是这样的——

Basic sales tax is applicable at a rate of 10% on all goods, except books, food, and medical products that are exempt.
Import duty is an additional sales tax applicable on all imported goods at a rate of 5%, with no exemptions.

基本销售税适用于所有商品,税率为 10%,但免税的书籍、食品和医疗产品除外。
进口税是适用于所有进口商品的附加销售税,税率为 5%,无豁免。

When I purchase items I receive a receipt which lists the name of all the items and their price (including tax), finishing with the total cost of the items, and the total amounts of sales taxes paid.
The rounding rules for sales tax are that for a tax rate of n%, a shelf price of p contains (np/100 rounded up to the nearest 0.05) amount of sales tax.

当我购买商品时,我会收到一张收据,其中列出了所有商品的名称及其价格(含税)、商品的总成本以及支付的销售税总额。
销售税的舍入规则是,对于 n% 的税率,p 的货架价格包含(np/100 向上舍入到最接近的 0.05)销售税金额。

“They told me, they are interested in the Design Aspectof your solution and would like to evaluate my Object Oriented Programming Skills.”

“他们告诉我,他们对您的解决方案的设计方面感兴趣,并想评估我的面向对象编程技能。”

This is what they told in their own words

这是他们用自己的话说的

  • For the solution, we would want you use either Java, Ruby or C#.
  • We are interested in the DESIGN ASPECT of your solution and would like to evaluate your Object Oriented Programming Skills.
  • You may use external libraries or tools for building or testing purposes. Specifically, you may use unit testing libraries or build tools available for your chosen language (e.g., JUnit, Ant, NUnit, NAnt, Test::Unit, Rake etc.)
  • Optionally, you may also include a brief explanation of your design and assumptions along with your code.
  • Kindly note that we are NOT expecting a web-based application or a comprehensive UI. Rather, we are expecting a simple, console based application and interested in your source code.
  • 对于解决方案,我们希望您使用 Java、Ruby 或 C#。
  • 我们对您的解决方案的设计方面很感兴趣,并希望评估您的面向对象编程技能
  • 您可以使用外部库或工具进行构建或测试。具体来说,您可以使用适用于您选择的语言的单元测试库或构建工具(例如,JUnit、Ant、NUnit、NAnt、Test::Unit、Rake 等)
  • 或者,您还可以包括对您的设计和假设的简要说明以及您的代码。
  • 请注意,我们不期待基于 Web 的应用程序或全面的 UI。相反,我们期待一个简单的、基于控制台的应用程序并且对您的源代码感兴趣。

So I provided below code – you can just copy paste code and run in VS.

所以我提供了下面的代码——你可以复制粘贴代码并在 VS 中运行。

class Program
 {
     static void Main(string[] args)
     {
         try
         {
             double totalBill = 0, salesTax = 0;
             List<Product> productList = getProductList();
             foreach (Product prod in productList)
             {
                 double tax = prod.ComputeSalesTax();
                 salesTax += tax;
                 totalBill += tax + (prod.Quantity * prod.ProductPrice);
                 Console.WriteLine(string.Format("Item = {0} : Quantity = {1} : Price = {2} : Tax = {3}", prod.ProductName, prod.Quantity, prod.ProductPrice + tax, tax));
             }
             Console.WriteLine("Total Tax : " + salesTax);
             Console.WriteLine("Total Bill : " + totalBill);                
        }
         catch (Exception ex)
         {
             Console.WriteLine(ex.Message);
         }
         Console.ReadLine();
     }

    private static List<Product> getProductList()
     {
         List<Product> lstProducts = new List<Product>();
         //input 1
         lstProducts.Add(new Product("Book", 12.49, 1, ProductType.ExemptedProduct, false));
         lstProducts.Add(new Product("Music CD", 14.99, 1, ProductType.TaxPaidProduct, false));
         lstProducts.Add(new Product("Chocolate Bar", .85, 1, ProductType.ExemptedProduct, false));

        //input 2
         //lstProducts.Add(new Product("Imported Chocolate", 10, 1, ProductType.ExemptedProduct,true));
         //lstProducts.Add(new Product("Imported Perfume", 47.50, 1, ProductType.TaxPaidProduct,true));

        //input 3
         //lstProducts.Add(new Product("Imported Perfume", 27.99, 1, ProductType.TaxPaidProduct,true));
         //lstProducts.Add(new Product("Perfume", 18.99, 1, ProductType.TaxPaidProduct,false));
         //lstProducts.Add(new Product("Headache Pills", 9.75, 1, ProductType.ExemptedProduct,false));
         //lstProducts.Add(new Product("Imported Chocolate", 11.25, 1, ProductType.ExemptedProduct,true));
         return lstProducts;
     }
 }

public enum ProductType
 {
     ExemptedProduct=1,
     TaxPaidProduct=2,
     //ImportedProduct=3
 }

class Product
 {
     private ProductType _typeOfProduct = ProductType.TaxPaidProduct;
     private string _productName = string.Empty;
     private double _productPrice;
     private int _quantity;
     private bool _isImportedProduct = false;

    public string ProductName { get { return _productName; } }
     public double ProductPrice { get { return _productPrice; } }
     public int Quantity { get { return _quantity; } }

    public Product(string productName, double productPrice,int quantity, ProductType type, bool isImportedProduct)
     {
         _productName = productName;
         _productPrice = productPrice;
         _quantity = quantity;
         _typeOfProduct = type;
         _isImportedProduct = isImportedProduct;
     }

    public double ComputeSalesTax()
     {
         double tax = 0;
         if(_isImportedProduct) //charge 5% tax directly
             tax+=_productPrice*.05;
         switch (_typeOfProduct)
         {
             case ProductType.ExemptedProduct: break;
             case ProductType.TaxPaidProduct:
                 tax += _productPrice * .10;
                 break;
         }
         return Math.Round(tax, 2);
         //round result before returning
     }
 }

you can uncommnet input and run for different inputs.

您可以取消输入并针对不同的输入运行。

I provided the solution but I was rejected.

我提供了解决方案,但被拒绝了。

"They said, they are unable to consider me for our current open positions because code solution is not satisfactory."

“他们说,由于代码解决方案不令人满意,他们无法考虑我担任我们目前的空缺职位。”

Please guide me what is missing here. Is this solution is not a good OOAD solution.
How can I improve my OOAD skills.
My seniors also says perfect OOAD application will also not work practically.

请指导我这里缺少什么。这个解决方案是不是一个好的 OOAD 解决方案。
我怎样才能提高我的 OOAD 技能。
我的前辈也说完美的 OOAD 应用程序实际上也行不通。

Thanks

谢谢

采纳答案by Eric Lippert

First off good heavens do not do financial calculations in double. Do financial calculations in decimal; that is what it is for. Use double to solve physicsproblems, not financialproblems.

首先,老天不会做双倍的财务计算。以十进制进行财务计算;这就是它的用途。使用 double 来解决物理问题,而不是财务问题。

The major design flaw in your program is that policy is in the wrong place. Who is in charge of computing the taxes? You've put the productin charge of computing the taxes, but when you buy an apple or a book or a washing machine, the thing you are about to buyis not responsible for telling you how much tax you're going to pay on it. Government policyis responsible for telling you that. Your design massively violates the basic OO design principle that objects should be responsible for their own concerns, and not anyone else's. The concern of a washing machine is washing your clothes, not charging the right import duty. If the tax laws change, you don't want to change the washing machine object, you want to change the policy object.

您程序中的主要设计缺陷是策略位于错误的位置。谁负责计算税款?你已经让产品负责计算税收,但是当你买一个苹果、一本书或一台洗衣机时,你要买的东西不负责告诉你你要支付多少税它。 政府政策负责告诉你这一点。你的设计严重违反了基本的 OO 设计原则,即对象应该为自己的关注点负责,而不是其他任何人的关注点。洗衣机关心的是洗你的衣服,而不是收取正确的进口税。如果税法改变,你不想改变洗衣机对象,要更改策略对象

So, how to approach these sorts of problems in the future?

那么,未来如何解决这些问题呢?

I would have started by highlighting every important noun in the problem description:

我会首先突出显示问题描述中的每个重要名词:

Basic sales taxis applicable at a rateof 10% on all goods, except books, food, and medical productsthat are exempt. Import dutyis an additional sales taxapplicable on all imported goodsat a rateof 5%, with no exemptions. When I purchase itemsI receive a receiptwhich lists the nameof all the itemsand their price(including tax), finishing with the total costof the items, and the total amounts of sales taxespaid. The rounding rules for sales taxare that for a tax rate of n%, a shelf priceof p contains (np/100 rounded up to the nearest 0.05) amount of sales tax.

基本的销售税是适用在速度的10%的所有产品,除了书籍食品医疗产品是免税。进口税是适用于所有进口商品的附加销售税税率为 5%,无豁免。当我购买商品时,我会收到一张收据,上面列出了所有商品名称及其价格含税),最后是总成本的项目,以及支付的销售税总额。销售税的舍入规则是,对于 n% 的税率,p的货架价格包含 (np/100 向上舍入到最接近的 0.05)销售税金额。

Now, what are the relationships between all those nouns?

现在,所有这些名词之间的关系是什么?

  • Basic Sales Tax is a kind of Sales Tax
  • Import Duty is a kind of Sales Tax
  • A Sales Tax has a Rate which is a Decimal
  • Books are a kind of Item
  • Food is a kind of Item
  • Medical Products are a kind of Item
  • Items may be Imported Goods
  • An Item has a Name which is a String
  • An Item has a Shelf Price which is a Decimal. (Note: does an item really have a price? two identical washing machines might be for sale for different prices at different stores, or at the same store at different times. A better design might be to say that a Pricing Policy relates an Item to its Price.)
  • A Sales Tax Exemption Policy describes the conditions under which a Sales Tax is inapplicable on an Item.
  • A Receipt has a list of Items, their prices and their taxes.
  • A Receipt has a total
  • A Receipt has a total tax
  • 基本销售税是销售税的一种
  • 进口税是一种销售税
  • 销售税的税率是十进制
  • 书是一种物品
  • 食物是一种物品
  • 医疗产品是一种物品
  • 物品可能是进口商品
  • 一个项目有一个名称,它是一个字符串
  • 一个项目有一个十进制的货架价格。(注意:一件商品真的有价格吗?两台相同的洗衣机可能在不同的商店以不同的价格出售,或者在同一家商店的不同时间出售。更好的设计可能是说定价政策将一件商品与它的价格。)
  • 销售税免税政策描述了销售税不适用于商品的条件。
  • 收据有项目清单、价格和税费。
  • 收据共有
  • 收据有总税额

... and so on. Once you have all the relationships between all the nouns worked out, then you can start designing a class hierarchy. There is an abstract base class Item. Book inherits from it. There is an abstract class SalesTax; BasicSalesTax inherits from it. And so on.

... 等等。一旦确定了所有名词之间的所有关系,就可以开始设计类层次结构。有一个抽象基类Item。本书继承了它。有一个抽象类 SalesTax;BasicSalesTax 继承自它。等等。

回答by Peter Cetinski

From a strictly OOA/D perspective, one major issue I see is that most of your class attributes have the redundant name of the class in the attribute name. e.g. productPrice, typeOf Product. In this case, everywhere you use this class you will have overly verbose and somewhat confusing code, e.g. product.productName. Remove the redundant class name prefix/suffixes from your attributes.

从严格的 OOA/D 角度来看,我看到的一个主要问题是您的大多数类属性在属性名称中都有类的冗余名称。例如产品价格,产品类型。在这种情况下,无论您在哪里使用这个类,您都会有过于冗长和有些混乱的代码,例如 product.productName。从您的属性中删除多余的类名前缀/后缀。

Also, I did not see any classes concerned with purchasing and creating a receipt as was asked in the question.

此外,我没有看到任何与问题中提到的购买和创建收据有关的课程。

回答by xxbbcc

This is highly subjective but here are a few points that I'd make about your code:

这是非常主观的,但我会对您的代码提出几点意见:

  • In my opinion you mixed Productand ShoppingCartItem. Productshould have the product name, tax status, etc. but not quantity. Quantity is not a property of a product - it'll be different for each customer of the company who buys that particular product.

  • ShoppingCartItemshould have a Productand the quantity. That way the customer can freely buy more or less of the same product. With your current setup that's not possible.

  • Calculating the final tax also shouldn't be part of the Product- it should be part of something like ShoppingCartsince the final tax calculation may involve knowing all products in the cart.

  • 在我看来你混合ProductShoppingCartItemProduct应该有产品名称、税务状态等,但没有数量。数量不是产品的属性 - 购买该特定产品的公司的每个客户都会有所不同。

  • ShoppingCartItem应该有一个Product和数量。这样客户就可以自由购买更多或更少的相同产品。使用您当前的设置,这是不可能的。

  • 计算最终税款也不应该是其中的一部分Product- 它应该是类似的一部分,ShoppingCart因为最终税款计算可能涉及了解购物车中的所有产品。

回答by Andrei G

Except the fact that you are using a class called product, you have not demonstrated you know what inheritance is, you have not created multiple classed inheriting from Product, no polymorphism. The problem could have been solved using multiple OOP concepts (even just to show that you know them). This is an interview problem so you want to show how much you know.

除了您使用一个名为 product 的类这一事实之外,您还没有证明您知道什么是继承,您没有创建从 Product 继承的多个类,没有多态性。这个问题可以使用多个 OOP 概念来解决(即使只是为了表明您了解它们)。这是一个面试问题,所以你想展示你知道多少。

I wouldn't however turn into depression right now. The fact that you didn't demonstrate them here does not mean you don't already know them or are not able to learn them.

然而,我现在不会变成抑郁症。您没有在此处演示它们的事实并不意味着您不了解它们或无法学习它们。

You just need a little more experience with either OOP or interviews.

您只需要更多的 OOP 或面试经验即可。

Good luck!

祝你好运!

回答by Radek

If company tells something about libraries like NUnit, JUnit or Test::Unit is more than probable that TDD is really importat to them. In your code sample is no tests at all.

如果公司对 NUnit、JUnit 或 Test::Unit 之类的库有所了解,则很可能 TDD 对他们来说真的很重要。在您的代码示例中根本没有测试。

I would try to demonstrate practical knowledge of:

我会尝试展示以下方面的实践知识:

  • Unit tests (eg. NUnit)
  • Mocking (eg. RhinoMocks)
  • Persistence (eg. NHibernate)
  • IoC Containers (eg. NSpring)
  • design patterns
  • SOLID principle
  • 单元测试(例如 NUnit)
  • 模拟(例如 RhinoMocks)
  • 持久性(例如 NHibernate)
  • IoC 容器(例如 NSpring)
  • 设计模式
  • 固体原则

I would like to recomend the www.dimecasts.netas impressive source of free, good quality screencasts which covers all above mentioned topics.

我想推荐www.dimecasts.net作为令人印象深刻的免费、高质量截屏视频来源,其中涵盖了上述所有主题。

回答by Chris Gessler

Here's a great example of an OO pattern for Products, Tax, etc... Notice the use of Interfaces, which is essential in OO design.

这是产品、税收等的 OO 模式的一个很好的例子...... 注意接口的使用,这在 OO 设计中是必不可少的。

http://www.dreamincode.net/forums/topic/185426-design-patterns-strategy/

http://www.dreamincode.net/forums/topic/185426-design-patterns-strategy/

回答by smonff

People who have start learning programming with OOP don't have great problems to understand what it means, because it is just as in real life. If you have skills with other programming familly than OO, it could be more difficult to understand.

开始使用 OOP 学习编程的人在理解它的含义方面没有太大的问题,因为它就像在现实生活中一样。如果您拥有 OO 以外的其他编程家族的技能,则可能更难理解。

First of all, turn-off your screen, or exit your favorite IDE. Take a paperand a penciland make a list of entities, relations, people, machines, processes, stuff, etc. everythingthat could be encountered into your final program.

首先,关闭你的屏幕,或者退出你最喜欢的 IDE。拿一张和一支铅笔,列出实体关系人员机器流程材料等,列出最终程序中可​​能会遇到的所有内容

Second, try to get the different basicentities. You will understand that some can share propertiesor abilities, you have to put it in abstract objects. You should start to draw a nice schema of your program.

其次,尝试获取不同的基本实体。你会明白有些可以共享 属性能力,你必须把它放在抽象对象中。您应该开始为您的程序绘制一个漂亮的架构。

Next you have to put fonctionnalities (methods, functions, subroutines, call it as you want): for example, a productobject should not be able to compute sales tax. A sales engineobject should.

接下来,您必须放置功能(方法、函数、子程序,您可以随意调用):例如,产品对象不应该能够计算销售税。一个销售引擎对象应该。

Don't feel trouble with all the big words(interfaces, properties, polymorphism, heritage, etc. ) and design patterns in a first time, don't even try to make beautiful code or whatever... Just think to simple objectsand interractions betweenit as in real life.

第一次不要对所有的大词(接口属性多态遗产等)和设计模式感到麻烦,甚至不要尝试编写漂亮的代码或其他什么......只要考虑简单的对象和它之间的互动就像在现实生活中一样

After, try to read some serious concise litterature about this. I think Wikipediaand Wikibooksare a really good way to begin and then just read stuff about GoF and Design Patternsand UML.

之后,尝试阅读一些关于此的严肃简洁的文献。我认为WikipediaWikibooks是一个非常好的开始方式,然后只需阅读有关GoF 和设计模式以及UML 的内容

回答by devdimi

A very good starting point about design rules are the SOLIDprinciples.

SOLID原则是设计规则的一个很好的起点。

For instance the Open Closed principle states that if you want to add new functionality you don't have to add code to existing class, but rather add new class.

例如,开放封闭原则指出,如果您想添加新功能,您不必向现有类添加代码,而是添加新类。

For your sample application this would mean that adding new sales tax would require adding new class. The same goes for different products that are exceptions to the rule.

对于您的示例应用程序,这意味着添加新的销售税需要添加新的类。对于规则例外的不同产品也是如此。

The rounding rule obviously goes in separate class - the Single Responsibility principle states that every class has a single responsibility.

舍入规则显然属于单独的类——单一职责原则规定每个类都有一个单一的职责。

I think trying to write the code yourself would bring by far more benefit than simply writing a good solution and pasting it here.

我认为尝试自己编写代码比简单地编写一个好的解决方案并将其粘贴在这里带来的好处要多得多。

A simple algorithm to write the perfect designed program would be:

编写完美设计程序的简单算法是:

  1. Write some code that solves the problem
  2. Check whether the code complies to the SOLID principles
  3. If there are rule violations than goto 1.
  1. 写一些代码来解决问题
  2. 检查代码是否符合SOLID原则
  3. 如果存在违反规则的情况,则转到 goto 1。

回答by Karthik

A perfect OOP implementation is completely debatable. From what I see in your question, you could modularize the code based on the role they perform to compute the final price like Product, Tax, ProductDB and so on.

一个完美的 OOP 实现是完全值得商榷的。从我在您的问题中看到的内容来看,您可以根据代码在计算最终价格(如 Product、Tax、ProductDB 等)所执行的角色进行模块化。

  1. Productcould be an abstract class and the derived types like Books, Food could be inherited from it. Tax applicability can be decided by the derived types. Product would tell whether the tax is applicable or not based on the derived class.

  2. TaxCriteriacan be an enum and the this can be specified during purchase (imported, Sales Tax applicability).

  3. Taxclass will compute tax based on TaxCriteria.

  4. Having a ShoppingCartItemas suggested by XXBBCCcan encapsulate Product and Tax instances and it is a great way to segregate product details with quantity, total price with tax etc.

  1. Product可以是一个抽象类,并且可以从它继承像 Books、Food 这样的派生类型。税收适用性可以由派生类型决定。产品将根据派生类判断税收是否适用。

  2. TaxCriteria可以是枚举,并且可以在购买时指定(进口,销售税适用性)。

  3. Tax类将根据TaxCriteria.

  4. 拥有XXBBCCShoppingCartItem建议的a可以封装 Product 和 Tax 实例,这是将产品详细信息与数量、总价与税等分开的好方法。

Good luck.

祝你好运。

回答by Jord?o

First of all, this is a very good interview question. It's a good gauge of manyskills.

首先,这是一个很好的面试问题。这是许多技能的一个很好的衡量标准。

There're many things you need to understand to provide a good answer (there is noperfect answer), both high-level and low-level. Here're a couple:

您需要了解很多事情才能提供一个好的答案(没有完美的答案),无论是高级还是低级。这是一对夫妇:

  • Domain Modeling-> how do you create a good model of the solution? What objects do you create? How will they solve the requirements? Looking for the nouns is a good start, but how do you decide if your choice of entities is good? What otherentities do you need? What domain knowledgedo you need to solve it?
  • Separation of concerns, loose coupling, high cohesion-> How do you separate out the parts of the design that have different concerns or rates of change and how do you relate them? How do you keep your design flexible and current?
  • Unit testing, refactoring, TDD-> What's your processfor coming up with a solution? Do you write tests, use mock objects, refactor, iterate?
  • Clean code, Language idioms-> Do you use the features of your programming language to help you? Do you write understandable code? Do your levels of abstraction make sense? How maintainable is the code?
  • Tools: Do you use source control? Build tools? IDEs?
  • 领域建模-> 你如何创建一个好的解决方案模型?你创建什么对象?他们将如何解决需求?查找名词是一个好的开始,但是您如何确定您选择的实体是否合适?您还需要哪些其他实体?你需要什么领域知识来解决它?
  • 关注点分离、松耦合、高内聚-> 你如何将设计中具有不同关注点或变化率的部分分开,你如何将它们关联起来?您如何保持设计的灵活性和最新性?
  • 单元测试、重构、TDD-> 您提出解决方案的过程是什么?您是否编写测试、使用模拟对象、重构、迭代?
  • 干净的代码,语言习语-> 你是否使用你的编程语言的特性来帮助你?你写出易于理解的代码吗?你的抽象层次有意义吗?代码的可维护性如何?
  • 工具:你使用源代码控制吗?构建工具?IDE?

From there, you can have many interesting discussions, involving design principles (like the SOLID principles), design patterns, analysis patterns, domain modeling, technology choices, future evolution paths (e.g. what if I add a database, or a rich UI layer, what needs to change?), trade-offs, non-functional requirements (performance, maintainability, security, ...), acceptance testing, etc...

从那里,您可以进行许多有趣的讨论,涉及设计原则(如 SOLID 原则)、设计模式、分析模式、领域建模、技术选择、未来的演进路径(例如,如果我添加数据库或丰富的 UI 层会怎样,什么需要改变?),权衡,非功能性需求(性能,可维护性,安全性,......),验收测试等......

I won't comment on how you should change your solution, just that you should focus more on these concepts.

我不会评论你应该如何改变你的解决方案,只是你应该更多地关注这些概念。

But, I can show you how I (partially) solved this problem, just as an example (in Java). Look in the Programclassto see how it all comes together to print this receipt:

但是,我可以向您展示我是如何(部分)解决这个问题的,只是作为一个例子(在 Java 中)。在Program班级中查看如何将这一切结合起来打印此收据:

------------------ THIS IS YOUR ORDER ------------------
(001)                Domain Driven Design -----   .99
(001)    Growing Object Oriented Software -----   .99
(001)                 House M.D. Season 1 -----   .99
(001)                 House M.D. Season 7 -----   .50
(IMD)    Growing Object Oriented Software -----    .50
(BST)                 House M.D. Season 1 -----    .00
(BST)                 House M.D. Season 7 -----    .45
(IMD)                 House M.D. Season 7 -----    .73
                                SUB-TOTAL -----  4.47
                                TAX TOTAL -----   .68
                                    TOTAL -----  5.15
---------------- THANKS FOR CHOOSING US ----------------

You should definitely take a look at those books :-)

你绝对应该看看那些书:-)

Just as a caveat: my solution is still very incomplete, I just focused on the happy path scenario in order to have a good foundation to build on.

需要注意的是:我的解决方案仍然非常不完整,我只是专注于快乐路径场景,以便有一个良好的基础。