tag:blogger.com,1999:blog-3237724005744642470.post8779015650530315430..comments2020-09-27T17:41:07.221+01:00Comments on Captain Debug's Blog: Disassembling Tell Don't AskRoger Hugheshttp://www.blogger.com/profile/07042290171112551665[email protected]Blogger6125tag:blogger.com,1999:blog-3237724005744642470.post-65437884211356705772013-04-22T08:16:16.758+01:002013-04-22T08:16:16.758+01:00The TDA principle says that you should not make an...The TDA principle says that you should not make any decisions using the object state. In your example the shopping cart price is not used to make any decisions. So it can be passed into the visa.PayBill() method. And this does not violate the TDA. However, the price also can be used to make a decision (e.g. we don't process any orders that are more than 1000 USD). I've written the following example that shows how to satisfy the TDA principle in this case. It's a bit verbose, but it is also very flexible.<br /><br />class OrderProcessor<br />{<br /> private IShoppingCartWithPriceApproval _shoppingCart;<br /> private IVisa _visa;<br /> private IWarehouse _warehouse;<br /> <br /> public OrderProcessor(IShoppingCartWithPriceApproval shoppingCart, IVisa visa, IWarehouse warehouse)<br /> {<br /> _shoppingCart = shoppingCart;<br /> _shoppingCart.PriceApproved += new PriceApprovedEventHandler(PayBill)<br /> <br /> _visa = visa;<br /> _visa.PaymentCompleted += new PaymentCompletedEventHandler(ShipOrder)<br /> <br /> _warehouse = warehouse;<br /> }<br /><br /> public void ProcessOrder()<br /> {<br /> _shoppingCart.CheckThePrice();<br /> }<br /> <br /> public void PayBill(double price)<br /> {<br /> _visa.PayBill(price);<br /> }<br /> <br /> public void ShipOrder()<br /> {<br /> _warehouse.ShipOrder();<br /> }<br />}<br /><br />public delegate void PriceApprovedEventHandler(double price);<br /><br />class ShoppingCartThatApprovesThePrice : ShoppingCart, IShoppingCartWithPriceApproval<br />{<br /> public event PriceApprovedEventHandler PriceApproved;<br /> <br /> public void CheckThePrice()<br /> {<br /> if (Price <= 1000)<br /> if (PriceApproved != null)<br /> PriceApproved(Price);<br /> }<br />}Nikita[email protected]tag:blogger.com,1999:blog-3237724005744642470.post-84532014964972740362012-03-16T10:10:50.946+00:002012-03-16T10:10:50.946+00:00Exactly, im my opinion having a method that just d...Exactly, im my opinion having a method that just delegates also breaks SRP.Brunohttps://www.blogger.com/profile/16206221290630279621[email protected]tag:blogger.com,1999:blog-3237724005744642470.post-91281950208525284032012-03-16T10:08:40.338+00:002012-03-16T10:08:40.338+00:00This is alone a topic for a huge post, but in my o...This is alone a topic for a huge post, but in my opinion it works as the following:<br /><br />Objects exist in different contexts, the context are defined in code as interfaces, a single interface defines methods that are compliant to SRP.<br /><br />Effectivly we have:<br /><br />class Cart {<br /><br />//item list and other fields<br /><br />}<br /><br />class CartInCheckoutContext extends Cart implements CheckoutContext {<br /> <br /> public CartInCheckoutContext(Cart cart);<br /> <br /> public boolean checkout(CheckoutStrategy strategy);<br />}<br /><br />in this way we don't break SRP and we comply to TDA. I know that this code looks some kind of ugly, but it is because it's written in Java. In Ruby or Scala, where we have mixins it would be much better.<br /><br />The TDA technique is in my opinion only a part of a broader paradigm called Data Context Interaction and the above code is an illustration of it.Wojciech Soczyńskihttps://www.blogger.com/profile/02817894419669090482[email protected]tag:blogger.com,1999:blog-3237724005744642470.post-9312341413358517082012-03-14T21:18:28.800+00:002012-03-14T21:18:28.800+00:00Wojciech
Thanks for the comment. I thought that so...Wojciech<br />Thanks for the comment. I thought that someone may mention this technique and wondered about mentioning it. It's certainly a technique I'd use, although I remain unconvinced about whether or not adding a method like:<br /><br />public boolean checkout(CheckoutStrategy strategy)<br /><br />to a ShoppingCart object breaks the SRP. Although the Cart doesn't contain any checkout code as that's held in the CheckoutStrategy, it still has a checkout method denoting that it has checkout responsibility. Hmmm.Roger Hugheshttps://www.blogger.com/profile/07042290171112551665[email protected]tag:blogger.com,1999:blog-3237724005744642470.post-54358648343668496212012-03-14T08:28:16.788+00:002012-03-14T08:28:16.788+00:00Nice post Roger. I can't totally agree more.
C...Nice post Roger. I can't totally agree more.<br />Cheers,<br />BrunoBrunohttps://www.blogger.com/profile/16206221290630279621[email protected]tag:blogger.com,1999:blog-3237724005744642470.post-74639379952064663572012-03-13T14:37:12.476+00:002012-03-13T14:37:12.476+00:00I think that you have missed the point a little. I...I think that you have missed the point a little. Indeed the cart should not be responsible for payment because it breaks SRP, but you can design it to still use the TDA and have single responsiblity. Enter strategy pattern:<br /><br />class Cart {<br /><br /> public boolean checkout(CheckoutStrategy strategy){<br /> return strategy.checkout(this.calculateCosts(),this.items);<br />}<br />}<br /><br />class CheckoutStrategy {<br /><br /> public CheckoutStrategy(CreditCardAdapter ca, WarehouseService wa);<br /><br />public boolean checkout(List<> items, Money cost){<br />if(ca.pay(cost)){<br />return wa.process(items);<br />}<br />return false;<br />}<br /><br />:)Wojciech Soczyńskihttps://www.blogger.com/profile/02817894419669090482[email protected]