Mükemmel OOP uygulaması nasıl oluşturulur [kapalı]


98

Geçenlerde bir 'x' şirketi için çalışıyordum. Bana bazı sorular gönderdiler ve sadece birini çözmemi söylediler.

Sorun şu ki -

Muaf tutulan kitaplar, yiyecekler ve tıbbi ürünler dışında tüm mallar için temel satış vergisi% 10 oranında uygulanır.
İthalat vergisi, muafiyet olmaksızın tüm ithal mallara% 5 oranında uygulanan ek bir satış vergisidir.

Öğeleri satın aldığımda, tüm öğelerin adını ve fiyatını (vergi dahil) listeleyen, öğelerin toplam maliyeti ve ödenen toplam satış vergisi tutarlarını içeren bir makbuz alırım.
Satış vergisi için yuvarlama kuralları,% n'lik bir vergi oranı için, p'lik bir raf fiyatının (en yakın 0.05'e yuvarlanmış np / 100) satış vergisi miktarını içermesidir.

" Çözümünüzün Tasarım Yönüyle ilgilendiklerini ve Nesne Yönelimli Programlama Becerilerimi değerlendirmek istediklerini söylediler ."

Bu kendi sözleriyle söyledikleri

  • Çözüm için Java, Ruby veya C # kullanmanızı istiyoruz.
  • Çözümünüzün TASARIM BOYUTU ile ilgileniyor ve Nesneye Yönelik Programlama Becerilerinizi değerlendirmek istiyoruz .
  • Dış kitaplıkları veya araçları oluşturmak veya test etmek için kullanabilirsiniz. Özellikle, birim test kitaplıklarını kullanabilir veya seçtiğiniz dil için mevcut araçlar oluşturabilirsiniz (örneğin, JUnit, Ant, NUnit, NAnt, Test :: Unit, Rake vb.)
  • İsteğe bağlı olarak, kodunuzla birlikte tasarımınızın ve varsayımlarınızın kısa bir açıklamasını da ekleyebilirsiniz.
  • Lütfen web tabanlı bir uygulama veya kapsamlı bir kullanıcı arayüzü beklemediğimizi unutmayın. Aksine, basit, konsol tabanlı bir uygulama bekliyoruz ve kaynak kodunuzla ilgileniyoruz.

Bu yüzden aşağıdaki kodu verdim - yapıştırma kodunu kopyalayıp VS'de çalıştırabilirsiniz.

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
     }
 }

girişi uncommnet ve farklı girişler için çalıştırabilirsiniz.

Çözümü sağladım ama reddedildim.

"Kod çözümü tatmin edici olmadığı için beni mevcut açık pozisyonlarımız için değerlendiremediklerini söylediler."

Lütfen burada eksik olanı bana yönlendirin. Bu çözüm iyi bir OOAD çözümü değil mi?
OOAD becerilerimi nasıl geliştirebilirim?
Son sınıf öğrencileri ayrıca mükemmel OOAD uygulamasının pratik olarak da çalışmayacağını söylüyor.

Teşekkürler


2
Belki de sizden ürün türleri arasında sıralama yerine miras hiyerarşisi kullanarak ayrım yapmanızı bekliyorlardı? (Bu yaklaşımın verilen senaryo için oldukça karmaşık olacağını düşünmeme rağmen.)
Douglas

Benim tahminim, sizin herhangi bir arayüz tanımlamadığınız için çözümünüzü yanlış bir şekilde reddettiler.
Chris Gessler

28
Genel bir kural olarak, bir görüşme durumunda birisi sizden OOP becerilerini göstermenizi isterse, bir geçiş ifadesi kullanmaktan kaçınmaya çalışmalısınız - bunun yerine bir miras hiyerarşisi kullanın.
Joe

4
Kod incelemesinde yayınlanmalıdır.
Derek

Oraya da göndermiştim ama orada iyi bir çözüm bulamadım. Ancak başkalarının yardımıyla oluşturduğum yeni çözümümü herkes görebilir codeproject.com/Questions/332077/… burada yeni kodumu da bulabilirsiniz.
sunder

Yanıtlar:


246

Öncelikle, iyi gökler iki kez mali hesaplamalar yapmazlar . Ondalık olarak finansal hesaplamalar yapın ; bunun için. Fizik problemlerini çözmek için finansal problemleri değil, duble kullanın .

Programınızdaki en büyük tasarım kusuru, politikanın yanlış yerde olmasıdır . Vergileri hesaplamaktan kim sorumlu? Sen koyduk ürünü vergileri hesaplama sorumlu, ancak bir elma veya bir kitap veya bir çamaşır makinesi, satın alırken satışın üzere olduğunuz şeyi size ödeme gidiyoruz ne kadar vergi söylüyorum sorumlu tutulamaz o. Bunu size söylemekten hükümet politikası sorumludur. Tasarımınız, nesnelerin başkalarının değil, kendi endişelerinden sorumlu olması gerektiği şeklindeki temel OO tasarım ilkesini büyük ölçüde ihlal ediyor . Bir çamaşır makinesinin endişesi, doğru ithalat vergisini talep etmek değil, giysilerinizi yıkamaktır. Vergi kanunları değişirse, değiştirmek istemezsinizçamaşır makinesi nesnesi , politika nesnesini değiştirmek istiyorsunuz .

Peki gelecekte bu tür sorunlara nasıl yaklaşılmalı?

Problem tanımındaki her önemli ismi vurgulayarak başlayacaktım:

Temel satış vergisi bir de geçerlidir oranda tümü üzerinde% 10 mal hariç kitaplar , gıda ve tıbbi ürünler muaftır. İthalat vergisi , ek bir satış vergi tümü üzerinde uygulanabilir ithal mallara bir de oran hiçbir ile% 5 muafiyetler . Öğeleri satın aldığımda , tüm öğelerin adını ve fiyatını ( vergi dahil ) listeleyen ve toplam maliyetle biten bir makbuz alıyorumve ödenen toplam satış vergisi tutarları . Satış vergisi için yuvarlama kuralları, % n'lik bir vergi oranı için, p'lik bir raf fiyatının (en yakın 0.05'e yuvarlanmış np / 100) satış vergisi miktarını içermesidir .

Şimdi, tüm bu isimler arasındaki ilişkiler nelerdir?

  • Temel Satış Vergisi bir tür Satış Vergisidir
  • İthalat Vergisi bir çeşit Satış Vergisidir
  • Bir Satış Vergisinin Ondalık bir Oranı vardır
  • Kitaplar bir tür Öğedir
  • Yemek bir çeşit Öğedir
  • Medikal Ürünler Bir Tür Öğedir
  • Öğeler İthal Mal Olabilir
  • Bir Öğenin Dize olan bir Adı vardır
  • Bir Öğenin Raf Fiyatı Ondalıktır. (Not: bir ürünün gerçekten bir fiyatı var mı? Farklı mağazalarda veya farklı zamanlarda aynı mağazada farklı fiyatlar için iki özdeş çamaşır makinesi satılıyor olabilir. Daha iyi bir tasarım, bir Fiyatlandırma Politikasının bir Öğeyi Ücret bu.)
  • Satış Vergisi Muafiyeti Politikası, bir Ürüne Satış Vergisinin uygulanamayacağı koşulları açıklar.
  • Makbuzda Ürünlerin bir listesi, fiyatları ve vergileri bulunur.
  • Bir Makbuzda toplam
  • Makbuzda toplam vergi vardır

... ve bunun gibi. Tüm isimler arasındaki tüm ilişkileri kurduğunuzda, bir sınıf hiyerarşisi tasarlamaya başlayabilirsiniz. Soyut bir temel sınıf Item vardır. Kitap ondan miras alır. Soyut bir SalesTax sınıfı vardır; BasicSalesTax bundan miras alır. Ve bunun gibi.


12
az önce sağlanandan daha fazlasına mı ihtiyacınız var? Kalıtımın nasıl uygulandığı ve polimorfizmin ne olduğu hakkında daha fazla şey öğrenmeniz gerekiyor gibi görünüyor.
Induster

27
@sunder: Bu cevap fazlasıyla yeterli. Belki de bunu ilk örnek olarak kullanarak becerilerinizi geliştirmek artık sizin sorumluluğunuzdadır. Örneğinizin gerçek hayattan bir örneğin tanımı olduğunu unutmayın. Gerçek hayattaki bir röportajda başarısız oldunuz çünkü bu gerçek hayat kodunun sizin sağlamadığınız bir gerçek hayat tasarımına ihtiyacı vardı.
Greg D

9
@Narayan: doubleDoğru cevabın% 0.00000001'i içinde olmanın fazlasıyla yeterli olduğu durumlar için idealdir. Yarım saniye sonra bir tuğlanın ne kadar hızlı düştüğünü bulmak istiyorsanız, matematiği çiftler halinde yapın. Bunu yaptığınızda mali çiftlerde arithemtic Eğer vergi sonrası fiyat gibi cevaplarla sona son derece yakın doğru cevabı olsa saçma 43,79999999999999 dolar ve bu sadece görünüşü.
Eric Lippert

31
+1 Belirtilen problemdeki her bir ismi incelemek ve ardından aralarındaki ilişkileri sıralamak olan dikkate değer bir alıştırmayı vurguladınız. İyi fikir.
Chris Tonkinson

3
@ Jordão: Ondalık olarak, 0.10'un on kez eklenmesi 1.00 verir. Ancak, 1.0 / 333.0 üç yüz otuz üç kez eklemek, ondalık veya çift olarak bir tane vermeyebilir. Ondalık olarak, paydada onluk kuvvetleri olan kesirler tam olarak temsil edilir; çiftlerde, ikiye katlanan kesirler. Yaklaşık olarak başka herhangi bir şey temsil edilir.
Eric Lippert

38

Şirket, NUnit, JUnit veya Test :: Unit gibi kitaplıklar hakkında bir şey söylerse, TDD'nin gerçekten onlara aktarılması olasıdır. Kod örneğinizde hiç test yok.

Aşağıdakilerle ilgili pratik bilgileri göstermeye çalışırdım:

  • Birim testleri (ör. NUnit)
  • Alay etme (ör. RhinoMocks)
  • Kalıcılık (ör. NHibernate)
  • IoC Konteynerleri (ör. NSpring)
  • tasarım desenleri
  • KATI prensibi

Www.dimecasts.net'i , yukarıda belirtilen tüm konuları kapsayan etkileyici ücretsiz, kaliteli ekran videolarının kaynağı olarak tavsiye etmek isterim .


19

Bu oldukça özneldir, ancak işte kodunuzla ilgili olarak belirteceğim birkaç nokta:

  • Bence karıştırdın Productve ShoppingCartItem. Productürün adı, vergi durumu vb. olmalıdır ancak miktarı olmamalıdır. Miktar bir ürünün özelliği değildir - o ürünü satın alan şirketin her müşterisi için farklı olacaktır.

  • ShoppingCartItema Productve miktarına sahip olmalıdır . Bu şekilde müşteri aynı üründen az ya da çok özgürce satın alabilir. Mevcut kurulumunuzla bu mümkün değil.

  • Nihai verginin hesaplanması da bunun bir parçası Productolmamalıdır - ShoppingCartnihai vergi hesaplaması, alışveriş sepetindeki tüm ürünleri bilmeyi gerektirebileceğinden , buna benzer bir şeyin parçası olmalıdır .


Bu cevapla ilgili tek sorunum, daha iyi bir ürün ödeme sisteminin nasıl kurulacağını (ki bu geçerli) açıklıyor, ancak OOP metodolojilerini gerçekten ayrıntılı olarak ele almıyor. Bu herhangi bir dilde uygulanabilir. Bir çeşit arayüz, kalıtım, polimorfizm vb. Göstermeden yine de testi geçemezdi.
Zaman Aşımı

Son noktaya göre: IMO vergi hesaplaması için en iyi yer, tek sorumluluk ilkesi nedeniyle ayrı TaxCalculator sınıfıdır.
Radek

Cevabınız için teşekkürler ama ne kadar pratik. her şirket bu kadar kapsamlı ve saf OOPS modellerinde çalışıyor.
sunder

@shyamsunder Cevabımda gerçekten saf hiçbir şey yok. OOD'nin önemli yönleri olan arayüzleri / kalıtımı kullanmaz, ancak en önemli ilkeyi gösterir - bence - ve bu, ait oldukları yere sorumlulukları koymaktır. Diğer yanıtların da işaret ettiği gibi, tasarımınızdaki temel sorun, sorumlulukları çeşitli aktörler arasında karıştırmanız ve bu, özellikler eklerken sorunlara yol açmasıdır. Çoğu büyük yazılım, ancak bu ilkeleri izlerlerse büyüyebilir.
xxbbcc

Güzel cevap ama vergi hesaplamasının ayrı bir amaç olması gerektiğine de katılıyorum.

14

Öncelikle bu çok güzel bir röportaj sorusu. Birçok becerinin iyi bir göstergesi .

İyi bir cevap verebilmek için anlamanız gereken pek çok şey vardır ( mükemmel bir cevap yoktur ), hem yüksek hem de düşük seviyede. İşte bir çift:

  • Etki Alanı Modellemesi -> çözümün iyi bir modelini nasıl yaratırsınız ? Hangi nesneleri yaratırsınız? Gereksinimleri nasıl çözecekler? İsim aramak iyi bir başlangıçtır, ancak varlık seçiminizin iyi olup olmadığına nasıl karar verirsiniz? Ne diğer kuruluşlar ihtiyacın var? Çözmek için hangi alan bilgisine ihtiyacınız var?
  • Kaygıların ayrılması, gevşek bağlantı, yüksek uyum -> Tasarımın farklı kaygıları veya değişim oranları olan kısımlarını nasıl ayırırsınız ve bunları nasıl ilişkilendirirsiniz? Tasarımınızı nasıl esnek ve güncel tutarsınız?
  • Birim testi, yeniden düzenleme, TDD -> Bir çözüm bulmak için süreciniz nedir ? Testler yazıyor musunuz, sahte nesneler kullanıyor musunuz, yeniden düzenleme yapıyor musunuz, yineliyor musunuz?
  • Temiz kod, Dil deyimleri -> Size yardımcı olması için programlama dilinizin özelliklerini kullanıyor musunuz? Anlaşılır bir kod yazıyor musunuz? Soyutlama seviyeleriniz mantıklı mı? Kod ne kadar bakım yapılabilir?
  • Araçlar : Kaynak kontrolü kullanıyor musunuz? Araçlar mı oluşturacaksınız? IDE'ler?

Buradan, tasarım ilkelerini (SOLID ilkeleri gibi), tasarım modellerini, analiz modellerini, alan modellemesini, teknoloji seçimlerini, gelecekteki gelişim yollarını (örneğin bir veritabanı veya zengin bir UI katmanı eklersem, nelerin değişmesi gerekiyor?), ödünleşmeler, işlevsel olmayan gereksinimler (performans, sürdürülebilirlik, güvenlik, ...), kabul testleri, vb.

Çözümünüzü nasıl değiştireceğiniz konusunda yorum yapmayacağım, sadece bu kavramlara daha fazla odaklanmalısınız.

Ancak, size bu sorunu nasıl (kısmen) çözdüğümü , bir örnek olarak (Java'da) gösterebilirim . Bu makbuzu yazdırmak için her şeyin nasıl bir araya geldiğini görmek için Programsınıfa bakın:

------------------ BU SİZİN SİPARİŞİNİZ ------------------
(001) Etki Alanı Odaklı Tasarım ----- 69,99 ABD Doları
(001) Büyüyen Nesne Yönelimli Yazılım ----- 49,99 Dolar
(001) House MD Sezon 1 ----- $ 29.99
(001) House MD 7. Sezon ----- $ 34.50
(IMD) Büyüyen Nesne Yönelimli Yazılım ----- 2,50 Dolar
(BST) House MD Sezon 1 ----- $ 3,00
(BST) House MD Sezon 7 ----- 3,45 Dolar
(IMD) House MD Sezon 7 ----- 1,73 Dolar
                                ALT TOPLAM ----- 184,47 $
                                VERGİ TOPLAMI ----- $ 10.68
                                    TOPLAM ----- 195,15 $
---------------- BİZİ TERCİH ETTİĞİNİZ İÇİN TEŞEKKÜRLER ----------------

Bu kitaplara kesinlikle bir göz atmalısınız :-)

Bir uyarı olarak: Çözümüm hala çok eksik, üzerine inşa edilecek iyi bir temele sahip olmak için sadece mutlu yol senaryosuna odaklandım.


Çözümünüzü inceledim ve oldukça ilginç buldum. Bir Reciept'i yazdırmaktan Order sınıfının sorumlu olmaması gerektiğini düşünüyorum. Benzer şekilde, TaxMethod sınıfı verginin hesaplanmasından sorumlu olmamalıdır. Ayrıca, TaxMethodPractice bir TaxMethod listesi içermemelidir. Bunun yerine, SalesPolicy adlı bir sınıf bu listeyi içermelidir. SalesEngine adlı bir sınıfa bir SalesPolicy, bir Order ve bir TaxCalculator geçirilmelidir. SalesEngine, Siparişteki ürünlere SatışPolitikasını uygulayacak ve Vergi Hesaplayıcısını kullanarak Vergiyi hesaplayacaktır
CKing

@bot: ilginç gözlemler .... Şu anda, Ordermakbuzu yazdırıyor, ancak Receiptkendi biçimlendirmesini biliyor. Ayrıca, TaxMethodPractice bir tür vergi politikasıdır, belirli bir senaryo için geçerli olan tüm vergileri tutar. TaxMethods olan vergi hesap makineleri. Yalnızca önerilen SalesEngine gibi daha yüksek düzeyde bir bağlama sınıfını kaçırdığınızı hissediyorum. Bu ilginç bir fikir.
Jordão

Her sınıfın iyi tanımlanmış tek bir sorumluluğu olması gerektiğini ve gerçek dünya nesnelerini temsil eden sınıfların gerçek dünya ile uyumlu bir şekilde davranması gerektiğini hissediyorum. Bu nedenle bir TaxMethod iki sınıfa ayrılabilir. Bir TaxCriteria ve bir TaxCalculator. Benzer şekilde, Sipariş bir makbuz yazdırmamalıdır. Bir makbuz oluşturmak için bir ReceiptGenerator'a bir Makbuz geçmesi gerekir.
CKing

@bot: Tamamen katılıyorum! İyi tasarımlar SAĞLAM ! Bir TaxMethod olan bir vergi hesap makinesi ve bir TaxEligibilityCheck olan bir vergi kriterleri. Ayrı varlıklardır. Makbuz gelince, evet, üreten parçayı bölmek tasarımı daha da geliştirecektir.
Jordão

1
Bu fikir spesifikasyon modelinden geliyor , bir göz atın!
Jordão

12

Ürün adı verilen bir sınıfı kullandığınız gerçeği dışında, kalıtımın ne olduğunu bildiğinizi göstermediniz, Üründen çok sayıda sınıflı kalıtım yaratmadınız, çok biçimlilik yok. Sorun birden fazla OOP kavramı kullanılarak çözülebilirdi (hatta sadece onları bildiğinizi göstermek için). Bu bir röportaj problemidir, bu yüzden ne kadar bildiğinizi göstermek istersiniz.

Ancak şu anda depresyona dönüşmezdim. Onları burada göstermemiş olmanız, onları zaten bilmediğiniz veya öğrenemeyeceğiniz anlamına gelmez.

OOP veya röportajlarla ilgili biraz daha deneyime ihtiyacınız var.

İyi şanslar!


aslında bu benim ilk tasarımımdı, başka bir tane yarattım ama karakter sınırı aşıldığı için size gösteremem.
sunder

bunu herhangi bir örnek yardımıyla gösterebilir misiniz?
sunder

@sunder: Soruyu yeni tasarımınızla güncelleyebilirsiniz.
Bjarke Freund-Hansen

10

OOP ile programlamayı öğrenmeye başlayan insanlar, bunun ne anlama geldiğini anlamak için büyük problemlerle karşılaşmazlar çünkü bu gerçek hayatta olduğu gibi . OO dışında başka programlama aileleri ile becerileriniz varsa, bunu anlamak daha zor olabilir.

Her şeyden önce, ekranınızı kapatın veya favori IDE'nizden çıkın. Bir atın kağıt ve kalem bir listesini ve yapmak varlıklar , ilişkiler , insanlar , makineler , süreçler , malzeme vb herşey son programa karşılaştı mümkündür.

İkinci olarak, farklı temel varlıkları almaya çalışın . Bazılarının özellikleri veya yetenekleri paylaşabileceğini anlayacaksınız , bunları soyut nesnelere koymanız gerekiyor . Programınızın güzel bir şemasını çizmeye başlamalısınız.

Daha sonra fonksiyonları (yöntemler, işlevler, alt yordamlar, istediğiniz gibi çağırın) koymanız gerekir: örneğin, bir ürün nesnesi satış vergisini hesaplayamamalıdır . Bir satış motoru nesnesi olmalıdır.

İlk seferde tüm büyük kelimelerle ( arayüzler , özellikler , çok biçimlilik , miras , vb.) Ve tasarım desenleriyle sorun yaşamayın , güzel kodlar yapmaya veya her neyse yapmaya çalışmayın ... Sadece basit nesneleri düşünün ve gerçek hayatta olduğu gibi aralarındaki etkileşimler .

Daha sonra, bununla ilgili ciddi ve kısa bir literatür okumaya çalışın. Wikipedia ve Wikibooks'un başlamak için gerçekten iyi bir yol olduğunu düşünüyorum ve ardından GoF ve Design Patterns ve UML hakkında bir şeyler okuyun .


3
"Her şeyden önce ekranınızı kapatın" için +1. Bence düşünme gücü, bilgi işlemin gücüyle sıklıkla karıştırılıyor.
kontur

1
Kalem ve kağıt kullanmanın en basit yaklaşımını benimsemek için +1. IDE'nin önünde otururken çoğu zaman insanların kafası karışıyor :)
Neeraj Gulia

Bazı bilim adamları, bir ekranı izlerken beynimizin dikkatsiz olduğunu söyledi. Yazılım mimarisi tasarımı okuduğumda, öğretmenimiz bizi kağıt üzerinde çalıştırıyor. Güçlü UML yazılımlarını umursamıyor. Önemli olan ilk önce şeyleri anlamaktır.
smonff

4

İlk önce karıştırmayın Product Makbuz (ile sınıf ShoppingCart) sınıfının, quantitybir parçası olmalıdır ReceipItem( ShoppingCartItemyanı sıra) Tax& Cost. TotalTax& TotalCostParçası olmalıdırShoppingCart .

Benim Productsınıf, yalnızca vardır Nameve Price& bazı salt okunur özellikleri gibi 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); } }
}

Vergi hesaplama bölümünüz ile birleştirilir Product . Bir Ürün, vergi politikalarını Vergi sınıfları olarak tanımlamaz. Sorunun açıklamasına göre iki tür Satış Vergisi vardır: Basicve Dutyvergiler. Bunu Template Method Design Patternbaşarmak için kullanabilirsiniz :

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; } }
}

Ve son olarak vergilerin uygulanacağı bir sınıf:

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));
        }

    }
}

Onları şurada deneyebilirsiniz MyFiddle'da deneyebilirsiniz .


2

Tasarım kuralları hakkında çok iyi bir başlangıç ​​noktası SOLID ilkeleridir.

Örneğin, Açık Kapalı ilkesi, yeni işlevler eklemek istiyorsanız mevcut sınıfa kod eklemeniz gerekmediğini, bunun yerine yeni sınıf eklemeniz gerektiğini belirtir.

Örnek uygulamanız için bu, yeni satış vergisi eklemenin yeni sınıf eklemeyi gerektireceği anlamına gelir. Aynısı, kuralın istisnası olan farklı ürünler için de geçerlidir.

Yuvarlama kuralı açıkça ayrı bir sınıfa girer - Tek Sorumluluk ilkesi her sınıfın tek bir sorumluluğu olduğunu belirtir.

Bence kodu kendiniz yazmaya çalışmanın, sadece iyi bir çözüm yazıp buraya yapıştırmaktan çok daha fazla fayda sağlayacağını düşünüyorum.

Mükemmel tasarlanmış programı yazmak için basit bir algoritma şöyle olacaktır:

  1. Sorunu çözen bir kod yazın
  2. Kodun SOLID ilkelerine uygun olup olmadığını kontrol edin
  3. Kural ihlalleri varsa 1'e gidin.

2

Mükemmel bir OOP uygulaması tamamen tartışmalıdır. Sorunuzda gördüğüm kadarıyla, Ürün, Vergi, ÜrünDB vb. Gibi nihai fiyatı hesaplamak için gerçekleştirdikleri role göre kodu modülerleştirebilirsiniz.

  1. Productsoyut bir sınıf olabilir ve Kitaplar, Yiyecek gibi türetilmiş türler ondan miras alınabilir. Vergi uygulanabilirliğine türetilen türlere göre karar verilebilir. Ürün, türetilen sınıfa göre verginin uygulanabilir olup olmadığını söyleyecektir.

  2. TaxCriteria bir sıralama olabilir ve bu satın alma sırasında belirtilebilir (ithal, Satış Vergisi uygulanabilirliği).

  3. Taxsınıf vergiyi temel alarak hesaplayacaktır TaxCriteria.

  4. XXBBCC'ninShoppingCartItem önerdiği gibi bir ürüne sahip olmak Ürün ve Vergi örneklerini kapsayabilir ve ürün ayrıntılarını miktarla, toplam fiyatı vergiyle vb. Ayırmanın harika bir yoludur.

İyi şanslar.


1

Kesinlikle OOA / D perspektifinden, gördüğüm önemli bir sorun, sınıf özniteliklerinizin çoğunun öznitelik adında sınıfın fazlalık adına sahip olmasıdır. ör. ürün Fiyatı, Ürün türü . Bu durumda, bu sınıfı kullandığınız her yerde aşırı derecede ayrıntılı ve biraz kafa karıştırıcı bir kodunuz olacak, örneğin product.productName. Gereksiz sınıf adı önekini / soneklerini özelliklerinizden kaldırın.

Ayrıca soruda sorulduğu gibi satın alma ve fatura oluşturma ile ilgili herhangi bir sınıf görmedim.


1

İşte Ürünler, Vergi vb. İçin OO modeline harika bir örnek ... OO tasarımında gerekli olan Arayüzlerin kullanımına dikkat edin.

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


3
Ürünü bir (soyut) sınıf yapmayı bir arayüz haline getirmeyi tercih ederim. Ben de her ürünü ayrı bir sınıf yapmazdım. En fazla kategori başına bir sınıf oluştururum.
CodesInChaos

@CodeInChaos - Çoğu zaman ikisine de ihtiyacınız var, ancak bir mimar olarak bir iş bulmaya çalışıyorsanız, Soyut bir sınıf üzerinden Arayüzler uygulamayı seçerim.
Chris Gessler

1
Bu örnekteki arayüzlerin hiçbir anlamı yok. Yalnızca onları uygulayan her sınıfta kod kopyasına yol açarlar. Her sınıf bunu aynı şekilde uygular.
Piotr Perak

0

Bir Ziyaretçi kalıbı kullanarak Vergili Maliyet problemine saldırdı.

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'a hoş geldiniz. Lütfen soruya cevabınızı açıkladığınızdan emin olun. OP sadece bir çözüm aramakla kalmaz, aynı zamanda bir çözümün neden daha iyi / daha kötü olduğunu gösterir.
Simon.SA
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.