Recentemente, estava tentando uma empresa 'x'. Eles me enviaram algumas perguntas e me disseram para resolver apenas uma.
O problema é assim -
O imposto básico sobre vendas é aplicável a uma alíquota de 10% sobre todas as mercadorias, exceto livros, alimentos e produtos médicos que estão isentos.
O direito de importação é um imposto adicional sobre vendas aplicável a todas as mercadorias importadas, à alíquota de 5%, sem isenções.
Quando compro itens, recebo um recibo que lista o nome de todos os itens e seu preço (incluindo impostos), finalizando com o custo total dos itens e os valores totais de impostos sobre vendas pagos.
As regras de arredondamento para imposto sobre vendas são que, para uma taxa de imposto de n%, um preço de prateleira de p contém (np / 100 arredondado para o 0,05 mais próximo) valor de imposto sobre vendas.
“Eles me disseram que estão interessados no aspecto de design da sua solução e gostariam de avaliar minhas habilidades de programação orientada a objetos .”
Isso é o que eles disseram em suas próprias palavras
- Para a solução, gostaríamos que você usasse Java, Ruby ou C #.
- Estamos interessados no ASPECTO DE DESIGN da sua solução e gostaríamos de avaliar suas habilidades de programação orientada a objetos .
- Você pode usar bibliotecas ou ferramentas externas para fins de construção ou teste. Especificamente, você pode usar bibliotecas de teste de unidade ou ferramentas de construção disponíveis para o idioma escolhido (por exemplo, JUnit, Ant, NUnit, NAnt, Test :: Unit, Rake etc.)
- Opcionalmente, você também pode incluir uma breve explicação de seu design e suposições junto com seu código.
- Observe que NÃO esperamos um aplicativo baseado na web ou uma interface do usuário abrangente. Em vez disso, esperamos um aplicativo simples baseado em console e interessado em seu código-fonte.
Então, eu forneci o código abaixo - você pode simplesmente copiar, colar o código e executar no 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
}
}
você pode descompactar a entrada e executar para entradas diferentes.
Eu forneci a solução, mas fui rejeitado.
"Eles disseram que não podem me considerar para nossas posições em aberto porque a solução de código não é satisfatória."
Por favor me guie o que está faltando aqui. Esta solução não é uma boa solução OOAD.
Como posso melhorar minhas habilidades OOAD.
Meus idosos também dizem que o aplicativo OOAD perfeito também não funcionará na prática.
obrigado
Respostas:
Em primeiro lugar, bom Deus, não fazem cálculos financeiros em dobro . Faça cálculos financeiros em decimal ; É pra isso que isto serve. Use o dobro para resolver problemas de física , não problemas financeiros .
A principal falha de design em seu programa é que a política está no lugar errado . Quem é o responsável pelo cálculo dos impostos? Você colocou o produto como responsável pelo cálculo dos impostos, mas quando você compra uma maçã ou um livro ou uma máquina de lavar, a coisa que você está prestes a comprar não é responsável por dizer quanto de imposto você vai pagar isto. A política governamental é responsável por lhe dizer isso. Seu projeto viola maciçamente o princípio básico de projeto OO de que os objetos devem ser responsáveis por seus próprios interesses , e não de outra pessoa. A preocupação da máquina de lavar é lavar a roupa, não cobrar o imposto de importação certo. Se as leis fiscais mudarem, você não vai querer mudarobjeto da máquina de lavar , você deseja alterar o objeto de política .
Então, como abordar esse tipo de problema no futuro?
Eu teria começado destacando todos os substantivos importantes na descrição do problema:
Agora, quais são as relações entre todos esses substantivos?
... e assim por diante. Depois de ter resolvido todas as relações entre todos os substantivos, você pode começar a projetar uma hierarquia de classes. Existe um Item de classe base abstrata. O livro herda disso. Existe uma classe abstrata SalesTax; BasicSalesTax herda dele. E assim por diante.
fonte
double
é ideal para situações em que estar dentro de 0,00000001% da resposta certa é mais do que suficiente. Se você quiser descobrir a velocidade com que um tijolo está caindo após meio segundo, faça as contas em duplas. Quando você faz aritêmica financeira em duplas, obtém respostas como o preço após os impostos é de 43,79999999999999 dólares e isso parece bobo, embora esteja extremamente próximo da resposta correta.Se a empresa contar algo sobre bibliotecas como NUnit, JUnit ou Test :: Unit, é mais do que provável que o TDD seja realmente importante para eles. Em sua amostra de código não há testes.
Eu tentaria demonstrar conhecimento prático de:
Eu gostaria de recomendar o www.dimecasts.net como uma fonte impressionante de screencasts gratuitos e de boa qualidade que cobrem todos os tópicos mencionados acima.
fonte
Isso é altamente subjetivo, mas aqui estão alguns pontos que eu faria sobre o seu código:
Na minha opinião, você misturou
Product
eShoppingCartItem
.Product
deve ter o nome do produto, situação fiscal, etc., mas não a quantidade. A quantidade não é uma propriedade de um produto - será diferente para cada cliente da empresa que comprar aquele produto específico.ShoppingCartItem
deve ter umProduct
e a quantidade. Dessa forma, o cliente pode comprar livremente mais ou menos do mesmo produto. Com sua configuração atual, isso não é possível.O cálculo do imposto final também não deve fazer parte do
Product
- deve ser parte de algo assim,ShoppingCart
pois o cálculo do imposto final pode envolver o conhecimento de todos os produtos no carrinho.fonte
Em primeiro lugar, esta é uma pergunta de entrevista muito boa. É um bom indicador de muitas habilidades.
Há muitas coisas que você precisa entender para fornecer uma boa resposta (não existe uma resposta perfeita), tanto de alto quanto de baixo nível. Aqui estão algumas:
A partir daí, você pode ter muitas discussões interessantes, envolvendo princípios de design (como os princípios SOLID), padrões de design, padrões de análise, modelagem de domínio, opções de tecnologia, caminhos de evolução futura (por exemplo, e se eu adicionar um banco de dados ou uma camada de IU rica, o que precisa mudar?), compromissos, requisitos não funcionais (desempenho, capacidade de manutenção, segurança, ...), teste de aceitação, etc ...
Não vou comentar como você deve mudar sua solução, apenas que você deve focar mais nesses conceitos.
Mas posso mostrar como resolvi (parcialmente) esse problema , apenas como exemplo (em Java). Olhe na
Program
aula para ver como tudo se junta para imprimir este recibo:Você definitivamente deveria dar uma olhada nesses livros :-)
Apenas como uma advertência: minha solução ainda está muito incompleta, eu apenas me concentrei no cenário do caminho feliz para ter uma boa base para construir.
fonte
Order
imprime o recibo, masReceipt
conhece a sua própria formatação. Além disso, TaxMethodPractice é uma espécie de política tributária, detém todos os impostos que se aplicam a um determinado cenário. TaxMethods são calculadoras de impostos. Eu sinto que você está perdendo apenas uma classe de ligação de nível superior , como seu SalesEngine proposto. É uma ideia interessante.Exceto o fato de que você está usando uma classe chamada produto, você não demonstrou que sabe o que é herança, você não criou várias classes herdadas de Produto, nenhum polimorfismo. O problema poderia ter sido resolvido usando vários conceitos OOP (mesmo apenas para mostrar que você os conhece). Este é um problema de entrevista, então você quer mostrar o quanto sabe.
No entanto, eu não entraria em depressão agora. O fato de não os ter demonstrado aqui não significa que ainda não os conheça ou não seja capaz de os aprender.
Você só precisa de um pouco mais de experiência com OOP ou entrevistas.
Boa sorte!
fonte
Pessoas que começaram a aprender programação com OOP não têm grandes problemas para entender o que isso significa, porque é exatamente como na vida real . Se você tiver habilidades com outra família de programação que não OO, pode ser mais difícil de entender.
Em primeiro lugar, desligue a tela ou saia do seu IDE favorito. Pegue um papel e um lápis e faça uma lista de entidades , relações , pessoas , máquinas , processos , coisas , etc. tudo que poderia ser encontrado em seu programa final.
Em segundo lugar, tente obter as diferentes entidades básicas . Você vai entender que alguns podem compartilhar propriedades ou habilidades , você tem que colocá-los em objetos abstratos . Você deve começar a desenhar um bom esquema do seu programa.
Em seguida, você deve colocar funcionalidades (métodos, funções, sub-rotinas, chame como quiser): por exemplo, um objeto de produto não deve ser capaz de calcular o imposto sobre vendas . Um objeto de mecanismo de vendas deveria.
Não sinta problemas com todas as palavras grandes ( interfaces , propriedades , polimorfismo , herança , etc.) e padrões de design em uma primeira vez, nem mesmo tente fazer um código bonito ou o que quer que seja ... Basta pensar em objetos simples e interações entre ele como na vida real .
Depois, tente ler alguma literatura séria e concisa sobre isso. Acho que a Wikipedia e o Wikilivros são uma ótima maneira de começar e depois apenas ler coisas sobre GoF e Design Patterns e UML .
fonte
Primeiro, não misture
Product
classe com classe Receipt (ShoppingCart
), oquantity
deve ser parte deReceipItem
(ShoppingCartItem
), bem comoTax
&Cost
. OTotalTax
&TotalCost
deve fazer parteShoppingCart
.Minha
Product
classe tem apenasName
&Price
& algumas propriedades somente leitura comoIsImported
:Sua parte de cálculo de impostos está associada
Product
. Um produto não define políticas fiscais, é classes de impostos. Com base na descrição do problema, existem dois tipos de impostos sobre vendas:Basic
eDuty
impostos. Você pode usarTemplate Method Design Pattern
para alcançá-lo:E, finalmente, uma aula para aplicar impostos:
Você pode experimentá-los em MyFiddle .
fonte
Um bom ponto de partida sobre as regras de projeto são os princípios SOLID .
Por exemplo, o princípio Aberto e Fechado afirma que se você deseja adicionar uma nova funcionalidade, você não precisa adicionar código à classe existente, mas sim adicionar uma nova classe.
Para seu aplicativo de exemplo, isso significaria que adicionar um novo imposto sobre vendas exigiria adicionar uma nova classe. O mesmo vale para diferentes produtos que são exceções à regra.
A regra de arredondamento obviamente está em classes separadas - o princípio de Responsabilidade Única afirma que cada classe tem uma única responsabilidade.
Acho que tentar escrever o código sozinho traria muito mais benefícios do que simplesmente escrever uma boa solução e colá-la aqui.
Um algoritmo simples para escrever o programa projetado perfeito seria:
fonte
Uma implementação OOP perfeita é completamente discutível. Pelo que vejo em sua pergunta, você pode modularizar o código com base na função que eles desempenham para calcular o preço final como Produto, Imposto, Banco de Produto e assim por diante.
Product
pode ser uma classe abstrata e os tipos derivados, como Livros, Comida, podem ser herdados dela. A aplicabilidade fiscal pode ser decidida pelos tipos derivados. O produto diria se o imposto é aplicável ou não com base na classe derivada.TaxCriteria
pode ser um enum e pode ser especificado durante a compra (importado, aplicabilidade do imposto sobre vendas).Tax
classe calculará o imposto com base emTaxCriteria
.Ter um
ShoppingCartItem
como sugerido por XXBBCC pode encapsular instâncias de produtos e impostos e é uma ótima maneira de segregar detalhes do produto com quantidade, preço total com impostos etc.Boa sorte.
fonte
De uma perspectiva estritamente OOA / D, um grande problema que vejo é que a maioria dos seus atributos de classe tem o nome redundante da classe no nome do atributo. por exemplo , preço do produto , tipo do produto . Nesse caso, em todos os lugares em que você usar esta classe, você terá um código muito detalhado e um tanto confuso, por exemplo, product.productName. Remova o prefixo / sufixo do nome da classe redundante de seus atributos.
Além disso, não vi nenhuma classe relacionada com a compra e criação de um recibo, como foi perguntado na pergunta.
fonte
Aqui está um ótimo exemplo de um padrão OO para Produtos, Impostos, etc ... Observe o uso de Interfaces, que é essencial no design OO.
http://www.dreamincode.net/forums/topic/185426-design-patterns-strategy/
fonte
Atacou o problema de Custo com Imposto usando um padrão de Visitante.
fonte