วิธีสร้างแอปพลิเคชัน OOP ที่สมบูรณ์แบบ [ปิด]


98

เมื่อเร็ว ๆ นี้ฉันกำลังพยายามหา บริษัท 'x' พวกเขาส่งชุดคำถามมาให้ฉันและบอกให้ฉันแก้เพียงข้อเดียว

ปัญหาเป็นเช่นนี้ -

ภาษีการขายขั้นพื้นฐานมีผลในอัตรา 10% สำหรับสินค้าทั้งหมดยกเว้นหนังสืออาหารและผลิตภัณฑ์ทางการแพทย์ที่ได้รับการยกเว้น
อากรขาเข้าคือภาษีขายเพิ่มเติมที่ใช้กับสินค้านำเข้าทั้งหมดในอัตรา 5% โดยไม่มีข้อยกเว้น

เมื่อฉันซื้อสินค้าฉันจะได้รับใบเสร็จรับเงินซึ่งแสดงชื่อของสินค้าทั้งหมดและราคา (รวมภาษี) จบด้วยต้นทุนรวมของสินค้าและจำนวนภาษีขายทั้งหมดที่จ่าย
กฎการปัดเศษสำหรับภาษีขายคือสำหรับอัตราภาษี n% ราคาชั้นวางของ p จะมีจำนวน (np / 100 ปัดเศษขึ้นเป็น 0.05 ที่ใกล้ที่สุด)

“ พวกเขาบอกฉันว่าพวกเขาสนใจในด้านการออกแบบของโซลูชันของคุณและต้องการประเมินทักษะการเขียนโปรแกรมเชิงวัตถุของฉัน”

นี่คือสิ่งที่พวกเขาบอกด้วยคำพูดของพวกเขาเอง

  • สำหรับวิธีแก้ปัญหาเราต้องการให้คุณใช้ Java, Ruby หรือ C #
  • เรามีความสนใจในด้านการออกแบบของการแก้ปัญหาของคุณและต้องการที่จะประเมินผลการศึกษาของคุณObject Oriented ทักษะการเขียนโปรแกรม
  • คุณอาจใช้ไลบรารีหรือเครื่องมือภายนอกเพื่อสร้างหรือทดสอบ โดยเฉพาะคุณอาจใช้ไลบรารีการทดสอบหน่วยหรือสร้างเครื่องมือที่มีให้สำหรับภาษาที่คุณเลือก (เช่น JUnit, Ant, NUnit, NAnt, Test :: Unit, Rake เป็นต้น)
  • คุณอาจใส่คำอธิบายสั้น ๆ เกี่ยวกับการออกแบบและสมมติฐานพร้อมกับรหัสของคุณก็ได้
  • โปรดทราบว่าเราไม่คาดหวังว่าจะมีแอปพลิเคชันบนเว็บหรือ UI ที่ครอบคลุม แต่เราคาดหวังว่าจะมีแอปพลิเคชันที่เรียบง่ายแบบคอนโซลและสนใจซอร์สโค้ดของคุณ

ดังนั้นฉันจึงให้โค้ดด้านล่าง - คุณสามารถคัดลอกวางโค้ดและเรียกใช้ใน 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
     }
 }

คุณสามารถ uncommnet อินพุตและรันสำหรับอินพุตที่แตกต่างกัน

ฉันให้วิธีแก้ปัญหา แต่ฉันถูกปฏิเสธ

"พวกเขากล่าวว่าพวกเขาไม่สามารถพิจารณาฉันสำหรับตำแหน่งที่เปิดอยู่ในปัจจุบันของเราได้เนื่องจากการแก้ปัญหาโค้ดไม่เป็นที่น่าพอใจ"

โปรดชี้แนะสิ่งที่ขาดหายไปที่นี่ โซลูชันนี้ไม่ใช่โซลูชัน OOAD ที่ดีหรือไม่
ฉันจะพัฒนาทักษะ OOAD ได้อย่างไร
รุ่นพี่ของฉันยังบอกด้วยว่าแอปพลิเคชัน OOAD ที่สมบูรณ์แบบจะไม่สามารถใช้งานได้จริง

ขอบคุณ


2
บางทีพวกเขาอาจคาดหวังให้คุณแยกแยะระหว่างประเภทผลิตภัณฑ์โดยใช้ลำดับชั้นการสืบทอดแทนการแจงนับ (แม้ว่าฉันคิดว่าแนวทางนั้นจะค่อนข้างซับซ้อนสำหรับสถานการณ์ที่กำหนด)
ดักลาส

ฉันเดาว่าพวกเขาปฏิเสธโซลูชันของคุณบ่อยครั้งเพราะคุณไม่ได้กำหนดอินเทอร์เฟซใด ๆ
Chris Gessler

28
ตามหลักทั่วไปหากมีคนถามคุณในสถานการณ์การสัมภาษณ์เพื่อแสดงทักษะ OOP คุณควรพยายามหลีกเลี่ยงการใช้คำสั่งสลับ - ใช้ลำดับชั้นการสืบทอดแทน
โจ

4
ควรโพสต์ในการตรวจสอบโค้ด
Derek

ฉันเคยโพสต์ไว้ที่นั่น แต่ไม่สามารถหาทางออกที่ดีได้ แต่ทุกคนสามารถเห็นโซลูชันใหม่ของฉันที่ฉันสร้างขึ้นหลังจากได้รับความช่วยเหลือจากผู้อื่น codeproject.com/Questions/332077/… ที่นี่คุณจะพบรหัสใหม่ของฉันด้วย
อาทิตย์

คำตอบ:


246

ก่อนปิดชั้นฟ้าทั้งหลายที่ดีไม่ได้ทำการคำนวณทางการเงินในคู่ ทำคำนวณทางการเงินในทศนิยม ; นั่นคือสิ่งที่มีไว้สำหรับ ใช้ double เพื่อแก้ปัญหาฟิสิกส์ไม่ใช่ปัญหาทางการเงิน

ข้อบกพร่องการออกแบบที่สำคัญในโปรแกรมของคุณคือการที่นโยบายการอยู่ในสถานที่ที่ไม่ถูกต้อง ใครเป็นผู้รับผิดชอบในการคำนวณภาษี? คุณได้กำหนดให้ผลิตภัณฑ์เป็นผู้รับผิดชอบในการคำนวณภาษี แต่เมื่อคุณซื้อแอปเปิ้ลหรือหนังสือหรือเครื่องซักผ้าสิ่งที่คุณกำลังจะซื้อจะไม่รับผิดชอบในการบอกคุณว่าคุณจะต้องจ่ายภาษีเท่าใด มัน. นโยบายของรัฐบาลมีหน้าที่บอกคุณว่า การออกแบบของคุณละเมิดหลักการออกแบบ OO พื้นฐานอย่างมากที่ว่าวัตถุควรรับผิดชอบต่อความกังวลของตนเองไม่ใช่ของใคร ข้อกังวลของเครื่องซักผ้าคือการซักผ้าโดยไม่เรียกเก็บภาษีนำเข้าที่ถูกต้อง หากกฎหมายภาษีมีการเปลี่ยนแปลงคุณไม่ต้องการเปลี่ยนแปลงวัตถุเครื่องซักผ้า , คุณต้องการเปลี่ยนวัตถุนโยบาย

ดังนั้นจะแก้ไขปัญหาเหล่านี้ในอนาคตได้อย่างไร?

ฉันจะเริ่มต้นด้วยการเน้นคำนามที่สำคัญทุกคำในคำอธิบายปัญหา:

ภาษีการขายพื้นฐานมีผลบังคับใช้ในอัตรา 10% ในทุกสินค้ายกเว้นหนังสือ , อาหารและผลิตภัณฑ์ทางการแพทย์ที่ได้รับการยกเว้น อากรขาเข้าเป็นเพิ่มภาษีการขายที่ใช้บังคับในทุกสินค้าที่นำเข้าในอัตรา 5% โดยไม่มีข้อยกเว้น เมื่อฉันซื้อรายการที่ผมได้รับใบเสร็จรับเงินซึ่งจะแสดงชื่อของทุกรายการของพวกเขาและราคา (รวมภาษี ) จบกับค่าใช้จ่ายทั้งหมดของสินค้าและจำนวนภาษีขายทั้งหมดที่จ่าย กฎการปัดเศษสำหรับภาษีการขายที่สำหรับอัตราภาษีของ n% เป็นราคาชั้นวางของหน้ามี (NP / 100 ปัดเศษขึ้นไปที่ใกล้ที่สุด 0.05) ปริมาณของภาษีการขาย

ตอนนี้คำนามทั้งหมดมีความสัมพันธ์กันอย่างไร?

  • ภาษีขายขั้นพื้นฐานคือภาษีขายชนิดหนึ่ง
  • อากรขาเข้าคือภาษีขายชนิดหนึ่ง
  • ภาษีขายมีอัตราซึ่งเป็นทศนิยม
  • หนังสือเป็นสิ่งของชนิดหนึ่ง
  • อาหารเป็นสิ่งของชนิดหนึ่ง
  • ผลิตภัณฑ์ทางการแพทย์เป็นสินค้าชนิดหนึ่ง
  • สินค้าอาจเป็นสินค้านำเข้า
  • รายการมีชื่อซึ่งเป็นสตริง
  • สินค้ามีราคาชั้นวางซึ่งเป็นทศนิยม (หมายเหตุ: สินค้ามีราคาจริง ๆ หรือไม่เครื่องซักผ้าที่เหมือนกัน 2 เครื่องอาจขายในราคาที่ต่างกันในร้านค้าต่างๆหรือในร้านเดียวกันในเวลาที่ต่างกันการออกแบบที่ดีกว่าอาจกล่าวได้ว่านโยบายการกำหนดราคาเกี่ยวข้องกับสินค้า ราคา)
  • นโยบายการยกเว้นภาษีการขายอธิบายถึงเงื่อนไขที่ภาษีขายไม่สามารถใช้กับสินค้าได้
  • ใบเสร็จรับเงินมีรายการสินค้าราคาและภาษี
  • ใบเสร็จรับเงินมีทั้งหมด
  • ใบเสร็จรับเงินมีภาษีทั้งหมด

... และอื่น ๆ เมื่อคุณมีความสัมพันธ์ทั้งหมดระหว่างคำนามทั้งหมดแล้วคุณสามารถเริ่มออกแบบลำดับชั้นของคลาสได้ มีไอเท็มคลาสพื้นฐานที่เป็นนามธรรม หนังสือสืบทอดมาจากมัน มี SalesTax ระดับนามธรรม BasicSalesTax สืบทอดจากมัน และอื่น ๆ


12
คุณต้องการมากกว่าที่ให้มาหรือเปล่า? ดูเหมือนว่าคุณต้องเรียนรู้เพิ่มเติมเกี่ยวกับวิธีการถ่ายทอดลักษณะทางพันธุกรรมและความหลากหลายคืออะไร
Induster

27
@sunder: คำตอบนี้เพียงพอแล้ว ตอนนี้เป็นความรับผิดชอบของคุณในการพัฒนาทักษะของคุณโดยอาจใช้สิ่งนี้เป็นตัวอย่างแรก โปรดทราบว่าตัวอย่างของคุณคือคำจำกัดความของตัวอย่างในชีวิตจริง คุณล้มเหลวในการสัมภาษณ์ในชีวิตจริงเนื่องจากรหัสในชีวิตจริงนี้ต้องการการออกแบบในชีวิตจริงที่คุณไม่ได้ให้ไว้
Greg D

9
@ Narayan: doubleเหมาะสำหรับสถานการณ์ที่อยู่ใน 0.00000001% ของคำตอบที่ถูกต้องมากเกินพอ หากคุณต้องการทราบว่าอิฐตกลงมาเร็วแค่ไหนหลังจากผ่านไปครึ่งวินาทีให้ทำคณิตศาสตร์เป็นสองเท่า เมื่อคุณทำเลขคณิตทางการเงินเป็นสองเท่าคุณจะได้รับคำตอบเช่นราคาหลังหักภาษีคือ 43.79999999999999 ดอลลาร์และนั่นก็ดูไร้สาระแม้ว่ามันจะใกล้เคียงกับคำตอบที่ถูกต้องก็ตาม
Eric Lippert

31
+1 คุณได้เน้นแบบฝึกหัดที่น่าทึ่งซึ่งก็คือการตรวจสอบคำนามแต่ละคำในปัญหาที่ระบุจากนั้นจึงแจกแจงความสัมพันธ์ระหว่างกัน ความคิดที่ดี.
Chris Tonkinson

3
@ Jordão: ในทศนิยมการเพิ่ม 0.10 สิบครั้งจะให้ 1.00 แต่การเพิ่ม 1.0 / 333.0 สามร้อยสามสิบสามครั้งไม่จำเป็นต้องให้หนึ่งในทศนิยมหรือสองเท่า ในทศนิยมเศษส่วนที่มีอำนาจเป็นสิบในตัวส่วนจะถูกแทนทั้งหมด ในคู่ผสมมันเป็นเศษส่วนที่มีกำลังสอง สิ่งอื่นจะแสดงโดยประมาณ
Eric Lippert

38

หาก บริษัท บอกอะไรบางอย่างเกี่ยวกับไลบรารีเช่น NUnit, JUnit หรือ Test :: Unit เป็นไปได้มากกว่าที่ TDD จะนำเข้ามาให้พวกเขาจริงๆ ในตัวอย่างโค้ดของคุณไม่มีการทดสอบเลย

ฉันจะพยายามแสดงความรู้เชิงปฏิบัติเกี่ยวกับ:

  • การทดสอบหน่วย (เช่น NUnit)
  • การล้อเลียน (เช่น RhinoMocks)
  • ความคงอยู่ (เช่น NHibernate)
  • คอนเทนเนอร์ IoC (เช่น NSpring)
  • รูปแบบการออกแบบ
  • หลักการ SOLID

ฉันขอแนะนำwww.dimecasts.netเป็นแหล่งที่น่าประทับใจของ screencasts คุณภาพดีฟรีซึ่งครอบคลุมหัวข้อที่กล่าวถึงข้างต้นทั้งหมด


19

สิ่งนี้มีความเป็นส่วนตัวสูง แต่นี่คือบางประเด็นที่ฉันจะทำเกี่ยวกับรหัสของคุณ:

  • ในความคิดของฉันคุณผสมProductและShoppingCartItem. Productควรมีชื่อผลิตภัณฑ์สถานะภาษี ฯลฯ แต่ไม่ใช่ปริมาณ ปริมาณไม่ใช่คุณสมบัติของผลิตภัณฑ์ แต่จะแตกต่างกันไปสำหรับลูกค้าแต่ละรายของ บริษัท ที่ซื้อผลิตภัณฑ์นั้น ๆ

  • ShoppingCartItemควรมีProductและปริมาณ ด้วยวิธีนี้ลูกค้าสามารถซื้อสินค้าชนิดเดียวกันได้มากหรือน้อย ด้วยการตั้งค่าปัจจุบันของคุณไม่สามารถทำได้

  • การคำนวณภาษีขั้นสุดท้ายไม่ควรเป็นส่วนหนึ่งของภาษีProduct- ควรเป็นส่วนหนึ่งของสิ่งต่างๆเช่นShoppingCartเนื่องจากการคำนวณภาษีขั้นสุดท้ายอาจเกี่ยวข้องกับการรู้สินค้าทั้งหมดในรถเข็น


ปัญหาเดียวที่ฉันมีกับคำตอบนี้คือการอธิบายวิธีสร้างระบบการชำระเงินผลิตภัณฑ์ที่ดีขึ้น (ซึ่งใช้ได้) แต่ไม่ได้อธิบายรายละเอียดเกี่ยวกับวิธีการ OOP อย่างละเอียด สามารถใช้งานได้ในทุกภาษา หากไม่มีการแสดงอินเทอร์เฟซการสืบทอดความหลากหลาย ฯลฯ เขาจะยังคงล้มเหลวในการทดสอบ
หมดเวลา

ในการอ้างอิงถึงประเด็นสุดท้าย: ตำแหน่งที่ดีที่สุดของ IMO สำหรับการคำนวณภาษีคือคลาส TaxCalculator ที่แยกจากกันเนื่องจากหลักการตอบสนองเดียว
เด็ค

ขอบคุณสำหรับการตอบกลับ แต่มันเป็นอย่างไร ทุก บริษัท ทำงานในรูปแบบ OOPS ที่ครอบคลุมและบริสุทธิ์
อาทิตย์

@shyamsunder ไม่มีอะไรที่บริสุทธิ์จริงๆเกี่ยวกับคำตอบของฉัน ไม่ใช้อินเทอร์เฟซ / การสืบทอดซึ่งเป็นส่วนสำคัญของ OOD แต่แสดงหลักการที่สำคัญที่สุด - ในความคิดของฉัน - และนั่นคือการวางความรับผิดชอบในที่ที่พวกเขาอยู่ ดังที่คำตอบอื่น ๆ ชี้ให้เห็นปัญหาหลักในการออกแบบของคุณคือการที่คุณผสมผสานความรับผิดชอบระหว่างนักแสดงหลายคนและจะนำไปสู่ปัญหาเมื่อเพิ่มคุณสมบัติ ซอฟต์แวร์ขนาดใหญ่ส่วนใหญ่จะเติบโตได้ก็ต่อเมื่อปฏิบัติตามหลักการเหล่านี้
xxbbcc

คำตอบที่ดี แต่ฉันก็เห็นด้วยว่าการคำนวณภาษีควรเป็นสิ่งที่แยกจากกัน

14

ก่อนอื่นนี่เป็นคำถามสัมภาษณ์ที่ดีมาก มันเป็นมาตรวัดที่ดีของหลายทักษะ

มีหลายสิ่งที่คุณต้องเข้าใจเพื่อให้ได้คำตอบที่ดี ( ไม่มีคำตอบที่สมบูรณ์แบบ) ทั้งระดับสูงและระดับต่ำ นี่คือสองสาม:

  • การสร้างแบบจำลองโดเมน -> คุณสร้างแบบจำลองที่ดีของโซลูชันได้อย่างไร? คุณสร้างวัตถุอะไร พวกเขาจะแก้ข้อกำหนดอย่างไร? การมองหาคำนามเป็นการเริ่มต้นที่ดี แต่คุณจะตัดสินใจได้อย่างไรว่าสิ่งที่คุณเลือกนั้นดีหรือไม่? คุณต้องการหน่วยงานอะไรอีกบ้าง? คุณต้องการความรู้ด้านโดเมนอะไรในการแก้ปัญหา
  • การแยกข้อกังวลการมีเพศสัมพันธ์ที่หลวมการยึดติดกันสูง -> คุณแยกส่วนของการออกแบบที่มีความกังวลหรืออัตราการเปลี่ยนแปลงที่แตกต่างกันอย่างไรและคุณเกี่ยวข้องอย่างไร? คุณทำให้การออกแบบของคุณยืดหยุ่นและเป็นปัจจุบันได้อย่างไร
  • การทดสอบหน่วยการปรับโครงสร้าง TDD -> กระบวนการของคุณในการหาวิธีแก้ปัญหาคืออะไร? คุณเขียนการทดสอบใช้วัตถุจำลอง refactor ทำซ้ำหรือไม่?
  • ทำความสะอาดรหัสสำนวนภาษา -> คุณใช้คุณสมบัติของภาษาโปรแกรมของคุณเพื่อช่วยคุณหรือไม่? คุณเขียนโค้ดที่เข้าใจได้หรือไม่? ระดับความเป็นนามธรรมของคุณสมเหตุสมผลหรือไม่? รหัสสามารถบำรุงรักษาได้อย่างไร?
  • เครื่องมือ : คุณใช้การควบคุมแหล่งที่มาหรือไม่? สร้างเครื่องมือ? IDE?

จากนั้นคุณสามารถสนทนาที่น่าสนใจมากมายซึ่งเกี่ยวข้องกับหลักการออกแบบ (เช่นหลักการของ SOLID) รูปแบบการออกแบบรูปแบบการวิเคราะห์การสร้างแบบจำลองโดเมนทางเลือกของเทคโนโลยีเส้นทางวิวัฒนาการในอนาคต (เช่นถ้าฉันเพิ่มฐานข้อมูลหรือเลเยอร์ UI ที่สมบูรณ์จะเป็นอย่างไร สิ่งที่ต้องเปลี่ยนแปลง), การแลกเปลี่ยน, ข้อกำหนดที่ไม่สามารถใช้งานได้ (ประสิทธิภาพ, ความสามารถในการบำรุงรักษา, ความปลอดภัย, ... ), การทดสอบการยอมรับ ฯลฯ ...

ฉันจะไม่แสดงความคิดเห็นว่าคุณควรเปลี่ยนวิธีแก้ปัญหาของคุณอย่างไรเพียง แต่คุณควรให้ความสำคัญกับแนวคิดเหล่านี้มากขึ้น

แต่ฉันสามารถแสดงให้คุณเห็นว่าฉัน (บางส่วน) แก้ไขปัญหานี้ได้อย่างไรเช่นเดียวกับตัวอย่าง (ใน Java) เข้าไปดูในProgramชั้นเรียนเพื่อดูว่าทั้งหมดมารวมกันเพื่อพิมพ์ใบเสร็จนี้ได้อย่างไร:

------------------ นี่คือคำสั่งซื้อของคุณ ------------------
(001) การออกแบบโดเมน ----- $ 69.99
(001) ซอฟต์แวร์เชิงวัตถุที่กำลังเติบโต ----- $ 49.99
(001) House MD Season 1 ----- $ 29.99
(001) House MD Season 7 ----- $ 34.50
(IMD) ซอฟต์แวร์เชิงวัตถุที่กำลังเติบโต ----- $ 2.50
(BST) House MD ซีซั่น 1 ----- $ 3.00
(BST) House MD Season 7 ----- $ 3.45
(IMD) House MD ซีซั่น 7 ----- $ 1.73
                                ย่อยรวม ----- $ 184.47
                                รวมภาษี ----- $ 10.68
                                    รวม ----- $ 195.15
---------------- ขอบคุณสำหรับการเลือกเรา ----------------

คุณควรดูหนังสือเหล่านั้นอย่างแน่นอน :-)

เช่นเดียวกับข้อแม้: วิธีแก้ปัญหาของฉันยังไม่สมบูรณ์ฉันแค่มุ่งเน้นไปที่สถานการณ์เส้นทางแห่งความสุขเพื่อที่จะมีรากฐานที่ดีในการต่อยอด


ฉันอ่านวิธีแก้ปัญหาของคุณแล้วและพบว่ามันน่าสนใจทีเดียว แม้ว่าฉันรู้สึกว่าคลาส Order ไม่ควรรับผิดชอบในการพิมพ์ Reciept ในทำนองเดียวกันคลาส TaxMethod ไม่ควรรับผิดชอบในการคำนวณภาษี นอกจากนี้ TaxMethodPractice ไม่ควรมีรายการ TaxMethod แต่คลาสที่เรียกว่า SalesPolicy ควรมีรายการนี้ คลาสที่เรียกว่า SalesEngine ควรส่งผ่าน SalesPolicy คำสั่งซื้อและ TaxCalculator SalesEngine จะใช้ SalesPolicy กับสินค้าในใบสั่งและคำนวณภาษีโดยใช้ TaxCalculator
CKing

@bot: ข้อสังเกตที่น่าสนใจ .... ตอนนี้Orderพิมพ์ใบเสร็จ แต่Receiptรู้เรื่องการจัดรูปแบบของตัวเอง นอกจากนี้ TaxMethodPractice ยังเป็นนโยบายภาษีโดยจะเก็บภาษีทั้งหมดที่ใช้กับสถานการณ์บางอย่าง TaxMethods คือเครื่องคำนวณภาษี ฉันรู้สึกว่าคุณขาดคลาสการผูกระดับที่สูงกว่าเท่านั้นเช่น SalesEngine ที่คุณเสนอ เป็นความคิดที่น่าสนใจ
Jordão

ฉันแค่รู้สึกว่าทุกชั้นเรียนต้องมีความรับผิดชอบที่กำหนดไว้อย่างดีเพียงอย่างเดียวและชั้นเรียนที่แสดงถึงวัตถุในโลกแห่งความเป็นจริงควรปฏิบัติตนในรูปแบบที่สอดคล้องกับโลกแห่งความเป็นจริง สำหรับเรื่องนั้น TaxMethod สามารถแบ่งออกเป็นสองชั้น TaxCriteria และ TaxCalculator ในทำนองเดียวกันคำสั่งซื้อจะต้องไม่พิมพ์ใบเสร็จรับเงิน ReceiptGenerator ควรส่งผ่านใบเสร็จรับเงินเพื่อสร้างใบเสร็จ
CKing

@bot: ฉันเห็นด้วยอย่างยิ่ง! การออกแบบที่ดีนั้นเป็นของแข็ง ! TaxMethod เป็นเครื่องคำนวณภาษีและ TaxEligibilityCheck เป็นเกณฑ์ภาษี พวกเขาเป็นหน่วยงานที่แยกจากกัน สำหรับใบเสร็จรับเงินใช่การแยกส่วนสร้างจะช่วยปรับปรุงการออกแบบเพิ่มเติม
Jordão

1
ความคิดนั้นมาจากรูปแบบสเปกลองดู!
เดา

12

ยกเว้นข้อเท็จจริงที่ว่าคุณกำลังใช้คลาสที่เรียกว่าผลิตภัณฑ์คุณยังไม่ได้แสดงให้คุณเห็นว่าการสืบทอดคืออะไรคุณไม่ได้สร้างคลาสหลายคลาสที่สืบทอดมาจากผลิตภัณฑ์ไม่มีความหลากหลาย ปัญหานี้สามารถแก้ไขได้โดยใช้แนวคิด OOP หลายแบบ (แม้เพียงเพื่อแสดงว่าคุณรู้จักพวกเขา) นี่เป็นปัญหาในการสัมภาษณ์ดังนั้นคุณจึงต้องการแสดงให้เห็นว่าคุณรู้มากแค่ไหน

อย่างไรก็ตามฉันจะไม่กลายเป็นโรคซึมเศร้าในตอนนี้ การที่คุณไม่ได้แสดงให้เห็นในที่นี้ไม่ได้หมายความว่าคุณไม่รู้จักพวกเขาหรือไม่สามารถเรียนรู้ได้

คุณเพียงแค่ต้องการประสบการณ์เพิ่มขึ้นอีกเล็กน้อยกับ OOP หรือการสัมภาษณ์

โชคดี!


อันที่จริงนี่เป็นการออกแบบครั้งแรกของฉันฉันสร้างขึ้นมาใหม่ แต่ไม่สามารถแสดงให้คุณเห็นได้เนื่องจากจำนวนอักขระเกิน
อาทิตย์

คุณสามารถแสดงให้เห็นได้ด้วยความช่วยเหลือของตัวอย่างใด ๆ
อาทิตย์

@sunder: คุณสามารถอัปเดตคำถามด้วยการออกแบบใหม่ของคุณ
Bjarke Freund-Hansen

10

ผู้ที่ได้เริ่มต้นเรียนรู้การเขียนโปรแกรมด้วย OOP ไม่ได้มีปัญหาที่ดีที่จะเข้าใจในสิ่งที่มันหมายถึงเพราะมันเป็นเช่นเดียวกับในชีวิตจริง หากคุณมีทักษะในการเขียนโปรแกรมอื่น ๆ นอกเหนือจาก OO อาจเป็นเรื่องยากที่จะเข้าใจ

ก่อนอื่นให้ปิดหน้าจอหรือออกจาก IDE ที่คุณชื่นชอบ ใช้กระดาษและดินสอและทำรายการของหน่วยงาน , ความสัมพันธ์ , คน , เครื่องจักร , กระบวนการ , สิ่งฯลฯทุกอย่างที่อาจจะพบในโปรแกรมสุดท้ายของคุณ

ประการที่สองพยายามรับเอนทิตีพื้นฐานที่แตกต่างกัน คุณจะเข้าใจว่าบางคนสามารถแบ่งปัน คุณสมบัติหรือความสามารถของคุณจะต้องใส่ไว้ในวัตถุที่เป็นนามธรรม คุณควรเริ่มวาดสคีมาที่ดีของโปรแกรมของคุณ

ถัดไปคุณต้องใส่ fonctionnalities (วิธีการฟังก์ชั่นซับรูทีเรียกว่าตามที่คุณต้องการ): ตัวอย่างเช่นผลิตภัณฑ์วัตถุไม่ควรจะสามารถที่จะคำนวณภาษีการขาย เครื่องมือการขายวัตถุที่ควร

ไม่รู้สึกว่ามีปัญหากับทุกคำใหญ่ ( อินเตอร์เฟซ , คุณสมบัติ , ความแตกต่าง , มรดก , ฯลฯ ) และรูปแบบการออกแบบในครั้งแรกไม่ได้พยายามที่จะทำให้รหัสที่สวยงามหรืออะไรก็ตาม ... เพียงแค่คิดว่าจะง่ายวัตถุและinterractions ระหว่างที่มันเป็นในชีวิตจริง

หลังจากนั้นลองอ่านบทความสั้น ๆ ที่กระชับเกี่ยวกับเรื่องนี้ ผมคิดว่าวิกิพีเดียและวิกิพีเดียเป็นวิธีที่ดีมากที่จะเริ่มต้นและจากนั้นเพียงแค่อ่านสิ่งที่เกี่ยวกับGoF และรูปแบบการออกแบบและUML


3
+1 สำหรับ "ก่อนอื่นปิดหน้าจอของคุณ" ฉันคิดว่าพลังแห่งความคิดมักเข้าใจผิดว่าเป็นพลังของการคำนวณ
kontur

1
+1 เพื่อใช้วิธีที่ง่ายที่สุดในการใช้ดินสอและกระดาษ หลายครั้งหลายคนสับสนเมื่อนั่งอยู่หน้า IDE :)
Neeraj Gulia

นักวิทยาศาสตร์บางคนกล่าวว่าสมองของเราไม่ได้ตั้งใจเมื่อดูหน้าจอ เมื่อฉันเรียนการออกแบบสถาปัตยกรรมซอฟต์แวร์อาจารย์ของเราให้เราทำงานบนกระดาษ เขาไม่สนใจโปรแกรม UML ที่มีประสิทธิภาพ สิ่งที่สำคัญคือต้องเข้าใจสิ่งต่างๆก่อน
smonff

4

ครั้งแรกไม่ผสมProductชั้นเรียนกับใบเสร็จรับเงิน ( ShoppingCart) ระดับที่quantityควรจะเป็นส่วนหนึ่งของReceipItem( ShoppingCartItem) เช่นเดียวกับและTax และควรเป็นส่วนหนึ่งของCostTotalTaxTotalCostShoppingCartควรเป็นส่วนหนึ่งของ

Productชั้นเรียนของฉันมีเพียงName& Price& คุณสมบัติอ่านอย่างเดียวเช่นIsImported:

class Product
{
    static readonly IDictionary<ProductType, string[]> productType_Identifiers = 
        new Dictionary<ProductType, string[]>
        {
            {ProductType.Food, new[]{ "chocolate", "chocolates" }},
            {ProductType.Medical, new[]{ "pills" }},
            {ProductType.Book, new[]{ "book" }}
        };

    public decimal ShelfPrice { get; set; }

    public string Name { get; set; }

    public bool IsImported { get { return Name.Contains("imported "); } }

    public bool IsOf(ProductType productType)
    {
        return productType_Identifiers.ContainsKey(productType) &&
            productType_Identifiers[productType].Any(x => Name.Contains(x));
    }
}

class ShoppringCart
{
    public IList<ShoppringCartItem> CartItems { get; set; }

    public decimal TotalTax { get { return CartItems.Sum(x => x.Tax); } }

    public decimal TotalCost { get { return CartItems.Sum(x => x.Cost); } }
}

class ShoppringCartItem
{
    public Product Product { get; set; }

    public int Quantity { get; set; }

    public decimal Tax { get; set; }

    public decimal Cost { get { return Quantity * (Tax + Product.ShelfPrice); } }
}

Productส่วนการคำนวณภาษีของคุณคู่กับ ผลิตภัณฑ์ไม่ได้กำหนดนโยบายภาษี แต่เป็นชั้นภาษี จากคำอธิบายของปัญหาภาษีขายมีสองประเภท: BasicและDutyภาษี คุณสามารถใช้Template Method Design Patternเพื่อให้บรรลุ:

abstract class SalesTax
{
    abstract public bool IsApplicable(Product item);
    abstract public decimal Rate { get; }

    public decimal Calculate(Product item)
    {
        if (IsApplicable(item))
        {
            //sales tax are that for a tax rate of n%, a shelf price of p contains (np/100)
            var tax = (item.ShelfPrice * Rate) / 100;

            //The rounding rules: rounded up to the nearest 0.05
            tax = Math.Ceiling(tax / 0.05m) * 0.05m;

            return tax;
        }

        return 0;
    }
}

class BasicSalesTax : SalesTax
{
    private ProductType[] _taxExcemptions = new[] 
    { 
        ProductType.Food, ProductType.Medical, ProductType.Book 
    };

    public override bool IsApplicable(Product item)
    {
        return !(_taxExcemptions.Any(x => item.IsOf(x)));
    }

    public override decimal Rate { get { return 10.00M; } }
}

class ImportedDutySalesTax : SalesTax
{
    public override bool IsApplicable(Product item)
    {
        return item.IsImported;
    }

    public override decimal Rate { get { return 5.00M; } }
}

และในที่สุดก็เป็นชั้นเรียนที่จะใช้ภาษี:

class TaxCalculator
{
    private SalesTax[] _Taxes = new SalesTax[] { new BasicSalesTax(), new ImportedDutySalesTax() };

    public void Calculate(ShoppringCart shoppringCart)
    {
        foreach (var cartItem in shoppringCart.CartItems)
        {
            cartItem.Tax = _Taxes.Sum(x => x.Calculate(cartItem.Product));
        }

    }
}

คุณสามารถลองพวกเขาออกที่MyFiddle


2

จุดเริ่มต้นที่ดีมากเกี่ยวกับกฎการออกแบบคือหลักการSOLID

ตัวอย่างเช่นหลักการ Open Closed ระบุว่าหากคุณต้องการเพิ่มฟังก์ชันใหม่คุณไม่จำเป็นต้องเพิ่มโค้ดลงในคลาสที่มีอยู่ แต่ให้เพิ่มคลาสใหม่

สำหรับแอปพลิเคชันตัวอย่างของคุณหมายความว่าการเพิ่มภาษีการขายใหม่จะต้องมีการเพิ่มคลาสใหม่ เช่นเดียวกันกับผลิตภัณฑ์ต่างๆที่เป็นข้อยกเว้นของกฎ

กฎการปัดเศษจะแยกกันอยู่ในชั้นเรียน - หลักการความรับผิดชอบเดียวระบุว่าทุกชั้นเรียนมีความรับผิดชอบเดียว

ฉันคิดว่าการพยายามเขียนโค้ดด้วยตัวเองจะก่อให้เกิดประโยชน์มากกว่าการเขียนคำตอบที่ดีและวางไว้ที่นี่

อัลกอริทึมง่ายๆในการเขียนโปรแกรมที่ออกแบบมาอย่างสมบูรณ์แบบคือ:

  1. เขียนโค้ดที่ช่วยแก้ปัญหา
  2. ตรวจสอบว่ารหัสเป็นไปตามหลักการ SOLID หรือไม่
  3. หากมีการละเมิดกฎมากกว่า goto 1.

2

การใช้งาน OOP ที่สมบูรณ์แบบเป็นที่ถกเถียงกันอย่างสมบูรณ์ จากสิ่งที่ฉันเห็นในคำถามของคุณคุณสามารถโมดูลาร์โค้ดตามบทบาทที่ดำเนินการเพื่อคำนวณราคาสุดท้ายเช่น Product, Tax, ProductDB และอื่น ๆ

  1. Productอาจเป็นคลาสนามธรรมและประเภทที่ได้รับเช่นหนังสืออาหารสามารถสืบทอดได้ การบังคับใช้ภาษีสามารถตัดสินใจได้ตามประเภทที่ได้รับ สินค้าจะบอกได้ว่าภาษีนั้นมีผลบังคับใช้หรือไม่ตามชั้นโดยสารที่ได้

  2. TaxCriteria สามารถเป็น enum และสามารถระบุได้ในระหว่างการซื้อ (นำเข้า, การบังคับใช้ภาษีขาย)

  3. TaxTaxCriteriaชั้นจะคำนวณภาษีขึ้นอยู่กับ

  4. การมีShoppingCartItemตามที่XXBBCCแนะนำสามารถห่อหุ้มอินสแตนซ์สินค้าและภาษีได้และเป็นวิธีที่ดีในการแยกรายละเอียดสินค้ากับปริมาณราคารวมพร้อมภาษีเป็นต้น

โชคดี.


1

จากมุมมอง OOA / D อย่างเคร่งครัดปัญหาสำคัญอย่างหนึ่งที่ฉันเห็นคือแอตทริบิวต์คลาสส่วนใหญ่ของคุณมีชื่อซ้ำซ้อนของคลาสในชื่อแอตทริบิวต์ เช่นสินค้าราคา typeof สินค้าสินค้าในกรณีนี้ทุกที่ที่คุณใช้คลาสนี้คุณจะมีโค้ดที่ละเอียดเกินไปและค่อนข้างสับสนเช่น product.productName ลบคำนำหน้าชื่อคลาสที่ซ้ำซ้อน / คำต่อท้ายออกจากแอตทริบิวต์ของคุณ

นอกจากนี้ฉันไม่เห็นชั้นเรียนใด ๆ ที่เกี่ยวข้องกับการซื้อและการสร้างใบเสร็จตามที่ถามในคำถาม


1

นี่คือตัวอย่างที่ดีของรูปแบบ OO สำหรับผลิตภัณฑ์ภาษีและอื่น ๆ โปรดสังเกตการใช้อินเทอร์เฟซซึ่งเป็นสิ่งสำคัญในการออกแบบ OO

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


3
ฉันต้องการทำให้ผลิตภัณฑ์เป็นคลาส (นามธรรม) มากกว่าที่จะทำให้เป็นอินเทอร์เฟซ ฉันจะไม่ทำให้แต่ละผลิตภัณฑ์เป็นชั้นเรียนแยกกัน อย่างมากฉันจะสร้างหนึ่งคลาสต่อหมวดหมู่
CodesInChaos

@CodeInChaos - เวลาส่วนใหญ่คุณต้องการทั้งสองอย่าง แต่ถ้าคุณกำลังพยายามหางานในฐานะสถาปนิกฉันจะเลือกใช้อินเทอร์เฟซในคลาส Abstract
Chris Gessler

1
อินเทอร์เฟซในตัวอย่างนี้ไม่มีความหมายเลย ซึ่งนำไปสู่การทำสำเนาโค้ดในแต่ละคลาสเท่านั้น แต่ละชั้นเรียนในลักษณะเดียวกัน
Piotr Perak

0

โจมตีปัญหาค่าใช้จ่ายด้วยภาษีโดยใช้รูปแบบผู้เยี่ยมชม

public class Tests
    {
        [SetUp]
        public void Setup()
        {
        }

        [Test]
        public void Input1Test()
        {
            var items = new List<IItem> {
                new Book("Book", 12.49M, 1, false),
                new Other("Music CD", 14.99M, 1, false),
                new Food("Chocolate Bar", 0.85M, 1, false)};

            var visitor = new ItemCostWithTaxVisitor();

            Assert.AreEqual(12.49, items[0].Accept(visitor));
            Assert.AreEqual(16.49, items[1].Accept(visitor));
            Assert.AreEqual(0.85, items[2].Accept(visitor));
        }

        [Test]
        public void Input2Test()
        {
            var items = new List<IItem> {
                new Food("Bottle of Chocolates", 10.00M, 1, true),
                new Other("Bottle of Perfume", 47.50M, 1, true)};

            var visitor = new ItemCostWithTaxVisitor();

            Assert.AreEqual(10.50, items[0].Accept(visitor));
            Assert.AreEqual(54.65, items[1].Accept(visitor));
        }

        [Test]
        public void Input3Test()
        {
            var items = new List<IItem> {
                new Other("Bottle of Perfume", 27.99M, 1, true),
                new Other("Bottle of Perfume", 18.99M, 1, false),
                new Medicine("Packet of headache pills", 9.75M, 1, false),
                new Food("Box of Chocolate", 11.25M, 1, true)};

            var visitor = new ItemCostWithTaxVisitor();

            Assert.AreEqual(32.19, items[0].Accept(visitor));
            Assert.AreEqual(20.89, items[1].Accept(visitor));
            Assert.AreEqual(9.75, items[2].Accept(visitor));
            Assert.AreEqual(11.80, items[3].Accept(visitor));
        }
    }

    public abstract class IItem : IItemVisitable
    { 
        public IItem(string name,
            decimal price,
            int quantity,
            bool isImported)
            {
                Name = name;
                Price = price;
                Quantity = quantity;
                IsImported = isImported;
            }

        public string Name { get; set; }
        public decimal Price { get; set; }
        public int Quantity { get; set; }
        public bool IsImported { get; set; }

        public abstract decimal Accept(IItemVisitor visitor);
    }

    public class Other : IItem, IItemVisitable
    {
        public Other(string name, decimal price, int quantity, bool isImported) : base(name, price, quantity, isImported)
        {
        }

        public override decimal Accept(IItemVisitor visitor) => Math.Round(visitor.Visit(this), 2);
    }

    public class Book : IItem, IItemVisitable
    {
        public Book(string name, decimal price, int quantity, bool isImported) : base(name, price, quantity, isImported)
        {
        }

        public override decimal Accept(IItemVisitor visitor) => Math.Round(visitor.Visit(this),2);
    }

    public class Food : IItem, IItemVisitable
    {
        public Food(string name, decimal price, int quantity, bool isImported) : base(name, price, quantity, isImported)
        {
        }

        public override decimal Accept(IItemVisitor visitor) => Math.Round(visitor.Visit(this), 2);
    }

    public class Medicine : IItem, IItemVisitable
    {
        public Medicine(string name, decimal price, int quantity, bool isImported) : base(name, price, quantity, isImported)
        {
        }

        public override decimal Accept(IItemVisitor visitor) => Math.Round(visitor.Visit(this), 2);
    }

    public interface IItemVisitable
    {
        decimal Accept(IItemVisitor visitor);
    }

    public class ItemCostWithTaxVisitor : IItemVisitor
    {
        public decimal Visit(Food item) => CalculateCostWithTax(item);

        public decimal Visit(Book item) => CalculateCostWithTax(item);

        public decimal Visit(Medicine item) => CalculateCostWithTax(item);

        public decimal CalculateCostWithTax(IItem item) => item.IsImported ?
            Math.Round(item.Price * item.Quantity * .05M * 20.0M, MidpointRounding.AwayFromZero) / 20.0M + (item.Price * item.Quantity)
            : item.Price * item.Quantity;

        public decimal Visit(Other item) => item.IsImported ?
            Math.Round(item.Price * item.Quantity * .15M * 20.0M, MidpointRounding.AwayFromZero) / 20.0M + (item.Price * item.Quantity)
            : Math.Round(item.Price * item.Quantity * .10M * 20.0M, MidpointRounding.AwayFromZero) / 20.0M + (item.Price * item.Quantity);
    }

    public interface IItemVisitor
    {
        decimal Visit(Food item);
        decimal Visit(Book item);
        decimal Visit(Medicine item);
        decimal Visit(Other item);
    }

ยินดีต้อนรับสู่ stackoverflow โปรดแน่ใจว่าคุณได้อธิบายคำตอบของคุณในการตอบคำถาม OP ไม่ได้เป็นเพียงการค้นหาวิธีแก้ปัญหา แต่ทำไมวิธีแก้ปัญหาถึงดีขึ้น / แย่ลง
Simon.SA
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.