Question
How do I adhere to the "Tell, Don't Ask" principle when performing a function involving multiple objects.
Example - Generating a Report
I have the following objects (illustrative purposes only):
Car, Horse, Rabbit
There is no relationship between these objects, but I do want to generate a Report based on these objects:
createHtmlReport(Car car, Horse horse, Rabbit rabbit){
Report report = new Report()
report.setSomeField(car.getSerialNumber())
report.setAnotherField(horse.getNumberOfLegs())
// ...etc
}
The problem with this method is that it has to "Pull" data from each object, which violates the "Tell, Don't Ask" rule. I would rather keep the insides of each object hidden, and have them generate a report for me:
car.createHtmlReport()
horse.createHtmlReport()
rabbit.createHtmlReport()
... but then I get 3 partial reports. Furthermore, I don't think a Rabbit should have to know how to generate every single report I need (HTML, JMS, XML, JSON ....).
Finally, whilst generating the report I may want to switch on multiple items:
if (car.getWheels() == 4 || horse.getLegs() == 4)
// do something
The report should maintain the ability to create its self.
In this case, each IReportable object should Implement void UpdateReport(Report aReport).
When Report.CreateReport(List<Reportable> aList) is invoked, it iterates through the List and each object in its own implementation of UpdateReport invokes:
aReport.AddCar(serialNumber)
aReport.AddHorse(horseName)
At the end of CreateReport, the report object should produce its own result.
The goal of "Tell don't ask" rule is to help you identify situations where the responsibility that should lie with the given object ends up being implemented outside of it (bad thing).
What responsibilities can we see in your case? What I see is:
1) knowing how to format the report (in xml, ascii, html, etc)
2) knowing what goes on which report
First one obviously does not belong with the domain object (Car, Horse etc.). Where should the 2) go? One could suggest the domain object but if there are multiple different reports in your system you end up burdening your objects with knowledge about differnt report details which would look and smell bad. Not to mention that it would violate the Single Responsibility Principle: being a Rabbit is one thing but knowing which parts of Rabbit information should go on report X vs. report Y is quite another.
Thus I would design classes which encapsulate data contents that go on a specific type of report (and possibly perform necessary calculations). I would not worry about them reading the data members of Rabbit, Horse or Car. The responsibility this class implements is 'gathering the data for a specific type of a report' which you've consciously decided should lie outside of the domain object.
That's exactly what the Visitor Pattern is for.
I don't know exactly this pattern's name (Visitor, Builder, ...):
public interface HorseView {
void showNumberOfLegs(int number);
}
public interface CarView {
void showNumberOfWheels(int number);
void showSerialNumber(String serialNumber);
}
public class Horse {
void show(HorseView view) {
view.showNumberOfLegs(this.numberOfLegs);
}
}
public class Car {
void show(CarView view) {
view.showNumberOfWheels(this.numberOfWheels);
view.showSerialNumber(this.serialNumber);
}
}
public class HtmlReport implements HorseView, CarView {
public void showNumberOfLegs(int number) {
...
}
public void showNumberOfWheels(int number) {
...
}
public void showSerialNumber(String serialNumber) {
...
}
}
public XmlModel implements HorseView, CarView {
...
}
public JsonModel implements HorseView, CarView {
...
}
This way you can have multiple representations of the same domain object, not violating "Tell don't ask" principle.
Related
I'm looking at a well-known problem and therefore there has to be a design pattern or a mix of patterns to solve it.
With the following classes and properties:
CTask
Name
Duration
TaskArea
CTaskArea
Name
CPerson
Name
Abilities
CAbility
Name
CTool
Name
CleaningTime
CConstraint
Name
Constraint
CTask, CPerson, CTool could have constraints e.g. Task A could only be done by persons with ability X, or person A could not do tasks of TaskArea X and so on.
For example, when I create a new CTask, CPerson or CTool I could imagine a constraint config dialog with dropdowns like:
Class | Operator | Class | Property | Value
CPerson | NOT | CTool | Name | Hammer
What design pattern provides the opportunity to dynamically configure constraints for all the classes, without forcing the classes to know additional information or take additional dependencies on each other?
Can I use an interface for objects to express that they accept constraints being applied somehow, or to discover classes which should be configurable with constraints?
Why not to have contraints_for_xxx property at each object having a constraint for particular xxx property?
When some child property is to be added into a collection, it is first run through constraints collection. If any constraint item returns false... exception is thrown, heaven thunders etc.
Constraints can be filled in object's constructor or later via some setupConstraints() call.
CPerson can look like (PHP example):
class Person
{
protected $constraintsAbc = null;
public function setConstraintsAbc(array $constraints)
{
$this->constraintsAbc = $constraints;
}
public function setABC($value)
{
foreach ($this->constraintsAbc as $constraint) {
if (!$constraint->isValid($value)) {
throw new Exception("Constraint {$constraint->getName()} is not happy with value $value");
}
}
$this->abc = $value;
}
}
class PersonSetup
{
public function setupPerson(Person $person)
{
$constrains[] = new PersonAbcConstraint("Value > 5");
$person->setContraintsABC($constrains);
}
}
This is, of course, fictious example. There is a problem here in some code duplication since you have constraintsAbc, setConstraintsAbc and setAbc as different hard-coded fields. But you can abstract this into some virtual "constraintable" field collection if you like.
this is the solution im ok with:
class CCouldHaveConstraints_Base
{
public virtual GetInstance();
public virtual GetClassName();
public virtual GetPropertyListThatCouldHaveConstraints();
}
class CPerson : CCouldHaveConstraints_Base
{
private String m_PersonName;
private String m_PersonAge;
public String PersonName
{
get {return this.m_PersonName;}
set {this.m_PersonName=value;}
}
public String PersonAge
{
get {return this.m_PersonAge;}
set {this.m_PersonAge=value;}
}
public override GetInstance()
{
return new CPerson;
}
public override GetClassName
{
return "Person";
}
public list<string> GetPropertyListThatCouldHaveConstraints()
{
list <string> ConstraintPropsList = new list<string>;
ConstraintPropsList.Add ("PersonName")
}
}
// class contains a list of all objects that could have constraints
class CConstraint_Lst
{
private list<CConstraint> m_ListOfConstraints;
private list<CCouldHaveConstraints_Base> m_ListOfObjectsThatCouldHaveConstraints;
}
// e.g Person | Person.Name | Tim | NOT | Tool | Tool.Name | "Hammer"
class CConstraint
{
private String m_ClassName_A;
private String m_ClassProperty_A;
private String m_ClassProperty_A_Value;
private String m_Operator;
private String m_ClassName_B;
private String m_ClassProperty_B;
private String m_ClassProperty_B_Value;
}
Is that enough code to figure out how im thinking?
Regards,
Tim
You've already made a great conceptual leap to model the constraints as CConstraint objects. The remaining core of the question seems to be "How do I then organize the execution of the constraints, provide them with the right inputs, and collect their outputs? (the outputs are constraint violations, validation errors, or warnings)"
CConstraints obviously can't be evaluated without any input, but you have some choices on how exactly to provide them with input, which we can explore with questions:
Do they get given a 'global state' which they can explore and look for violations in?
Or do they get given a tuple of objects, or object graph, which they return a success or failure result for?
How do they signal constraint violations? Is it by throwing exceptions, returning results, adding them to a collection of violations, or removing violating objects from the world, or triggering repair rules?
Do they provide an "explanation" output that helpfully explains which object or combination of objects is the offending combination, and what rule it violates?
Compilers might be an interesting place to look for inspiration. We know a good compiler processes some complicated input, and produces one or more easy-to-understand error messages allowing the programmer to fix any problem in their program.
Compilers often have to choose some pattern of organizing the work that they're doing like recursion (recursive descent), or a visitor pattern (visit a tree of objects in some arrangement), or stateful pattern matching on a stream of input approach (syntax token recognition by regex matching, or processing a stream of characters), or a chain-of-responsibility (one processor validates and processes input, passes it to the next processor in the chain). Which is actually a whole family of design patterns you can choose from.
Probably one of the most flexible patterns to look at which is useful for your case is the visitor pattern, because you can extend your domain model with additional classes, all of which know how to do a 'visiting' phase, which is basically what 'validation' often entails - someone visits all the objects in a scenario, and inspects their properties, with an easily extensible set of logics (the validation rules) specific to those types of objects, without needing to worry about the mechanics of the visiting procedure (how you traverse the object graph) in each validation rule.
Let's say you need to build an application that manages cheques. Each cheque contains data about the amount of money, the date, the payee and an additional payment date which may or may not be present. Additionally, each cheque must be related to a current account which belongs to a certain bank.
Now, our application should allow cheques printing under these conditions:
Each bank managed by the app has a different cheque layout (i.e. each field has a different x,y position).
The cheque layout changes slightly if the payment date is present, even with the same related bank object. But, from bank to bank these changes may not be the same (e.g. bank A may vary position for the date field, while bank B changes position for the payee field)
With these restrictions in place, it's difficult to come up with a simple inheritance schema as there is no consistent behavior to factor out accross the different types of cheques there are. One possible solution would be to avoid inheritance and create a class for every cheque - bank combination:
class ChequeNoPaymentDateForBankA
class ChequeWithPaymentDateForBankA
class ChequeNoPaymentDateForBankB
class ChequeWithPaymentDateForBankB, etc.
Each of these classes implement the print() method which takes the fields positions from a Bank object and builds up the cheque layout. So far so good, but this approach leaves me with a strange feeling as there is no room for code reuse. I wonder if I'm misinterpreting the problem and perhaps there is a better way. As this is not a new problem domain at all, I'm sure this is a reinvent-the-wheel effort. Any insights will be kindly appreciated.
Usually in these situations I move from inheritance to delegation. That is, instead of putting the common code in a superclass (which, as you say, is problematic becuase there are two dimensions), I put the common in a field (one field per dimension) and delegate to that field.
Assuming you're speaking about Java:
public interface Bank {
public void print();
}
public class BankA implements Bank {
public void print() { ... }
}
public class BankB implements Bank {
public void print() { ... }
}
public interface PaymentSchedule {
public void print();
}
public class WithPaymentDate implements PaymentSchedule {
public void print() { ... }
}
public class NoPaymentDate implements PaymentSchedule {
public void print() { ... }
}
public class Cheque {
private final Bank bank;
private final PaymentSchedule schedule;
public Cheque(Bank b, PaymentSchedule s) {
bank = b;
schedule = s;
}
public void print() {
bank.print();
schedule.print();
}
}
That's the general structure of the solution.
Depending on the exact details of your print() algorithm you may need to pass some more data into the print methods and/or to pass this data into the constructors of the classes (of the Bank or PaymentSchedule subclasses) and store it in fields.
I would start by separating the domain model (cheques, banks, etc) from the view (the way the cheques are printed). This is the basic idea behind the MVC pattern and one of its aims is to allow the same domain model to be displayed in different ways (which seems to be your case). So, I would first create the domain classes, something like:
class Cheque
{
protected $bank;
protected $money;
...
}
class Bank {...}
Note that these classes are the "M" of the MVC triad and implement the logic of your domain model, not the behavior related to the rendering process. The next step would be to implement the View classes used to render a cheque. Which approach to take heavily depends on how complex your rendering is, but I would start by having a ChequeView class that renders the common parts and that delegates to other sub-view the specific parts that can change (in this case the date):
abstract class ChequeView
{
protected $cheque;
protected $dateView;
public function __construct($cheque)
{
$this->cheque = $cheque;
$this->dateView = //Whatever process you use to decide if the payment date is shown or not
}
public function render()
{
$this->coreRender();
$this->dateView->render();
}
abstract protected function coreRender();
}
class BankACheckView extends ChequeView
{
protected function coreRender() {...}
}
class BankBCheckView extends ChequeView
{
protected function coreRender() {...}
}
abstract class DateView
{
abstract function render()
}
class ShowDateView extends DateView
{
function render() {...}
}
class NullDateView extends DateView
{
function render() {...}
}
And, if there is code to reuse across subclasses, you can of course factor them in ChequeView and make coreRender() call them.
In case your rendering turns to be too complex, this design may not scale. In that case I would go for splitting your view in meaningful subparts (e.g. HeaderView, AmountView, etc) so that rendering a cheque becomes basically rendering its different sub-parts. In this case the ChequeView may end basically working as a Composite. Finally, if you reach this case and setting up the ChequeView turns out to be a complex task you may want to use a Builder.
Edit based on the OP comments
The Builder is mostly used when the instantiation of the final object is a complex process (e.g. there are many things to sync between the sub-parts in order to get a consistent whole). There is generally one builder class and different clients, that send messages (potentially in different orders and with different arguments) to create a variety of final objects. So, while not prohibited, it is not usual to have one builder per type of object that you want to build.
If you are looking for a class that represents the creation of a particular instance you may want to check the Factory family of patterns (maybe the Abstract Factory resembles closely to what you had in mind).
HTH
While reading about abstraction, I came across the following statement
"Abstraction captures only those details about an object that are relevant to the current perspective"
For eg.
From the driver's perspective, Car class would be
public class Car
{
void start();
void applybrakes();
void changegear();
void stop();
}
From the mechanic's perspective, Car class would be
public class Car
{
void changeOil();
void adjustBrakes();
}
My question,
While designing a system, do we design for one user perspective(either driver or mechanic) or can
we design for multiple user perspective and further abstract out based on user type?
Hope my question is clear.
Thanks
Depending on your use case you might need to deign for multiple users. In your example, if your car will be used by both the mechanic and the driver, then you cannot just ignore one set of users. In that case, you can still abstract details by using Interfaces.
You could design your object like this:
interface IDrivable {
void start();
void applyBrakes();
void changeGear();
void stop();
}
interface IFixable {
void changeOil();
void adjustBrakes();
}
public class Car : IDrivable, IFixable {
// implement all the methods here
}
Now, when a mechanic wants the car, you don't give him a Car object, instead give him an IFixable object. Similarly, the driver gets an IDrivable object. This keeps the relevant abstraction for both sets of users simultaneously.
class Driver {
private IDrivable car;
public Driver(IDrivable car) {
this.car = car;
}
public driveCar() {
this.car.start();
this.car.accelerate();
//this is invalid because a driver should not be able to do this
this.car.changeOil();
}
}
Similary, a mechanic won't have access to the methods in the interface IDrivable.
You can read more about interfaces here. Even though this is the MSDN link and uses C#, all major languages support interfaces.
I think you may be inferring too much from "perspective." I wouldn't take perspective here to mean a person or user so much as a vantage point. The idea of a view here is maybe not even a good metaphor. What we're really talking about here is division of responsibility between the smaller objects that we use to compose the larger objects.
The whole point of this idea is decoupling and modularity. You want objects that you can pull out and replace without changing everything around them. So you want your objects to be coherent, for their methods and variables to be closely related.
You might be able to get some mileage from the user metaphor in terms of the interface-client relationship between objects.
I'm trying to follow the Law Of Demeter ( see http://en.wikipedia.org/wiki/Law_of_Demeter , http://misko.hevery.com/code-reviewers-guide/flaw-digging-into-collaborators/ ) as I can see the benefits, however I've become a little stuck when it comes to domain objects.
Domain objects do naturally have a chain and sometimes it's necessary to display the information about the entire chain.
For instance, a shopping basket:
Each order contains a user, delivery info and a list of items
Each order item contains a product and quantity
Each product has a name and price.
Each user contains a name and address
The code which displays the order information has to use all the information about the order, users and products.
Surely it's better and more reusable to get this information through the order object e.g. "order.user.address.city" than for some code higher up to do queries for all the objects I listed above then pass them into the code separately?
Any comments/suggestions/tips are welcome!
One problem with using chained references, such as order.user.address.city, is that higher-order dependencies get "baked into" the structure of code outside the class.
Ideally, in cases when you refactor your class, your "forced changes" should be limited to the methods of the class being refactored. When you have multiple chained references in the client code, refactoring drives you to make changes in other places of your code.
Consider an example: suppose that you'd like to replace User with an OrderPlacingParty, an abstraction encapsulating users, companies, and electronic agents that can place an order. This refactoring immediately presents multiple problems:
The User property will be called something else, and it will have a different type
The new property may not have an address that has city in cases when the order is placed by an electronic agent
The human User associated with the order (suppose that your system needs one for legal reasons) may be related to the order indirectly, - for example, by being a designated go-to person in the definition of the OrderPlacingParty.
A solution to these problems would be to pass the order presentation logic everything that it needs directly, rather than having it "understand" the structure of the objects passed in. This way you would be able to localize the changes to the code being refactored, without spreading the changes to other code that is potentially stable.
interface OrderPresenter {
void present(Order order, User user, Address address);
}
interface Address {
...
}
class PhysicalAddress implements Address {
public String getStreetNumber();
public String getCity();
public String getState();
public String getCountry();
}
class ElectronicAddress implements Address {
public URL getUrl();
}
interface OrderPlacingParty {
Address getAddress();
}
interface Order {
OrderPlacingParty getParty();
}
class User implements OrderPlacingParty {
}
class Company implements OrderPlacingParty {
public User getResponsibleUser();
}
class ElectronicAgent implements OrderPlacingParty {
public User getResponsibleUser();
}
I think, when chaining is used to access some property, it is done in two (or at least two) different situation. One is the case that you have mentioned, for example, in your presentation module, you have an Order object and you would like to just display the owner's/user's address, or details like city. In that case, I think it is of not much problem if you do so. Why? Because you are not performing any business logic on the accessed property, which can (potentially) cause tight coupling.
But, things are different if you use such chaining for the purpose of performing some logic on the accessed property. For example, if you have,
String city = order.user.address.city;
...
order.user.address.city = "New York";
This is problematic. Because, this logic is/should more appropriately be performed in a module closer to the target attribute - city. Like, in a place where the Address object is constructed in the first place, or if not that, at least when the User object is constructed (if say User is the entity and address the value type). But, if it goes farther than that, the farther it goes, the more illogical and problematic it becomes. Because there are too many intermediaries are involved between the source and the target.
Thus, according to the the Law of Demeter, if you are performing some logic on the "city" attribute in a class, say OrderAssmebler, which accesses the city attribute in a chain like order.user.address.city, then you should think of moving this logic to a place/module closer to the target.
You're correct and you'll most likely model your value objects something like this
class Order {
User user;
}
class User {
Address shippingAddress;
Address deliveryAddress;
}
class Address {
String city;
...
}
When you start considering how you will persist this data to a database (e.g. ORM) do you start thinking about performance. Think eager vs lazy loading trade offs.
Generally speaking I adhere to the Law of Demeter since it helps to keep changes in a reduced scope, so that a new requirement or a bug fix doesn't spread all over your system. There are other design guidelines that help in this direction, e.g. the ones listed in this article. Having said that, I consider the Law of Demeter (as well as Design Patterns and other similar stuff) as helpful design guidelines that have their trade-offs and that you can break them if you judge it is ok to do so. For example I generally don't test private methods, mainly because it creates fragile tests. However, in some very particular cases I did test an object private method because I considered it to be very important in my app, knowing that that particular test will be subject to changes if the implementation of the object changed. Of course in those cases you have to be extra careful and leave more documentation for other developers explaining why you are doing that. But, in the end, you have to use your good judgement :).
Now, back to the original question. As far as I understand your problem here is writing the (web?) GUI for an object that is the root of a graph of objects that can be accessed through message chains. For that case I would modularize the GUI in a similar way that you created your model, by assigning a view component for each object of your model. As a result you would have classes like OrderView, AddressView, etc that know how to create the HTML for their respective models. You can then compose those views to create your final layout, either by delegating the responsibility to them (e.g. the OrderView creates the AddressView) or by having a Mediator that takes care of composing them and linking them to your model. As an example of the first approach you could have something like this (I'll use PHP for the example, I don't know which language you are using):
class ShoppingBasket
{
protected $orders;
protected $id;
public function getOrders(){...}
public function getId(){...}
}
class Order
{
protected $user;
public function getUser(){...}
}
class User
{
protected $address;
public function getAddress(){...}
}
and then the views:
class ShoppingBasketView
{
protected $basket;
protected $orderViews;
public function __construct($basket)
{
$this->basket = $basket;
$this->orederViews = array();
foreach ($basket->getOrders() as $order)
{
$this->orederViews[] = new OrderView($order);
}
}
public function render()
{
$contents = $this->renderBasketDetails();
$contents .= $this->renderOrders();
return $contents;
}
protected function renderBasketDetails()
{
//Return the HTML representing the basket details
return '<H1>Shopping basket (id=' . $this->basket->getId() .')</H1>';
}
protected function renderOrders()
{
$contents = '<div id="orders">';
foreach ($this->orderViews as $orderView)
{
$contents .= orderViews->render();
}
$contents .= '</div>';
return $contents;
}
}
class OrderView
{
//The same basic pattern; store your domain model object
//and create the related sub-views
public function render()
{
$contents = $this->renderOrderDetails();
$contents .= $this->renderSubViews();
return $contents;
}
protected function renderOrderDetails()
{
//Return the HTML representing the order details
}
protected function renderOrders()
{
//Return the HTML representing the subviews by
//forwarding the render() message
}
}
and in your view.php you would do something like:
$basket = //Get the basket based on the session credentials
$view = new ShoppingBasketView($basket);
echo $view->render();
This approach is based on a component model, where the views are treated as composable components. In this schema you respect the object's boundaries and each view has a single responsibility.
Edit (Added based on the OP comment)
I'll assume that there is no way of organizing the views in subviews and that you need to render the basket id, order date and user name in a single line. As I said in the comment, for that case I would make sure that the "bad" access is performed in a single, well documented place, leaving the view unaware of this.
class MixedView
{
protected $basketId;
protected $orderDate;
protected $userName;
public function __construct($basketId, $orderDate, $userName)
{
//Set internal state
}
public function render()
{
return '<H2>' . $this->userName . "'s basket (" . $this->basketId . ")<H2> " .
'<p>Last order placed on: ' . $this->orderDate. '</p>';
}
}
class ViewBuilder
{
protected $basket;
public function __construct($basket)
{
$this->basket = $basket;
}
public function getView()
{
$basketId = $this->basket->getID();
$orderDate = $this->basket->getLastOrder()->getDate();
$userName = $this->basket->getUser()->getName();
return new MixedView($basketId, $orderDate, $userName);
}
}
If later on you rearrange your domain model and your ShoppingBasket class can't implement the getUser() message anymore then you will have to change a single point in your application, avoid having that change spread all over your system.
HTH
The Law Of Demeter is about calling methods, not accessing properties/fields. I know technically properties are methods, but logically they're meant to be data. So, your example of order.user.address.city seems fine to me.
This article is interesting further reading: http://haacked.com/archive/2009/07/13/law-of-demeter-dot-counting.aspx
A common red flag that an OOP language is not being leveraged properly looks like this:
if (typeof(x) == T1)
{
DoSomethingWithT1(x);
}
else if (typeof(x) == T2)
{
DoSomethingWithT2(x);
}
The standard "fix" for such design issues is to make T1 and T2 both share an interface, either through inheritance of a base type or implementation of a common interface (in languages that support it); for example, in C# a solution might be:
public interface IT
{
void DoSomething();
}
However, sometimes you want to implement functionality that differs based on the type of an object but that functionality does not belong within that object's type; thus polymorphism seems the wrong way to go.
For example, consider the case of a UI that provides a view of a given clump of data. Supposing this view is capable of rendering various layouts and controls depending on the type of data being presented, how would you implement this type-specific rendering without a bunch of if/else statements?
For reasons that I hope are obvious, putting the rendering logic in the type itself strikes me as a very bad decision in this case. On the other hand, without coupling the type of data object to its visual presentation I have a hard time seeing how the if/else scenario is avoided.
Here's a concrete example: I work on a trading application which utilizes many different pricing models for various market products. These different models are represented by types inheriting from a common PricingModel base; and each type is associated with a completely different set of parameters. When the user wants to view the parameters for a particular pricing model (for a particular product), currently these are displayed by a form which detects the type of the model and displays an appropriate set of controls. My question is how this could be implemented more elegantly than it is currently (with a big if/else block).
I realize this probably seems like a very basic question; it's just one of those gaps in my knowledge (of solid OOP principles? design patterns? common sense?) that I figured it's about time to fix.
We are injecting (Spring.Net) such functionality into dictionaries by type.
IDictionary<Type, IBlahImplementor> blahImplementors;
blahImplementors[thingy.GetType()].Do(thingy);
This dictionary could be managed by a kind of repository which provides the functionality.
As an implementation detail, the implementor usually knows the type it depends on an can provide it itself:
interface IBlahImplementor
{
Type ForType { get; }
void Do(object thingy);
}
Then it is added to the dictionary like this:
IEnumerably<IBlahImplementor> blahImplementors;
foreach (var implementor in blahImplementors)
{
blahImplementors.Add(implementor.ForType, implementor);
}
Remark: IMHO, it is very important to understand that some things do NOT belong into a class, even if providing subtype-specific implementations would make life much easier.
Edit: Finally understood your concrete example.
It is actually about instancing the right UI control to show the pricing models parameters. It should be possible with the pattern I described above. If you don't have a single UI control for a pricing model, you either create it or you write a UI configurer or something like this which sets up the required controls.
interface IPricingModelUiConfigurer
{
Type PricingModelType { get; }
void SetupUi(Control parent, IPricingModel model);
}
you can use common interface approach as you describe and Command pattern to trigger methods with "functionality does not belong within that object's type". I think this won't break solid OOP principles.
What you described is pretty much exactly the use case for the Visitor Pattern.
EDIT: For your concrete example, you could apply the visitor pattern like this:
// interface used to add external functionality to pricing models
public interface PricingModelVisitor {
void visitPricingModel1(PricingModel1 m);
void visitPricingModel2(PricingModel2 m);
...
}
// your existing base-class, with added abstract accept() method to accept a visitor
public abstract class PricingModelBase {
public abstract void accept(PricingModelVisitor v);
...
}
// concrete implementations of the PricingModelBase implement accept() by calling the
// appropriate method on the visitor, passing themselves as the argument
public class PricingModel1 : PricingModelBase {
public void accept(PricingModelVisitor v) { v.visitPricingModel1(this); }
...
}
public class PricingModel2 : PricingModel {
public void accept(PricingModelVisitor v) { v.visitPricingModel2(this); }
...
}
// concrete implementation of the visitor interface, in this case with the new
// functionality of adding the appropriate controls to a parent control
public class ParameterGuiVisitor : PricingModelVisitor {
private Control _parent;
public ParameterGuiVisitor(Control parent) { _parent = parent; }
visitPricingModel1(PricingModel1 m) {
// add controls to _parent for PricingModel1
}
visitPricingModel2(PricingModel2 m) {
// add controls to _parent for PricingModel1
}
}
now, instead of using a big if-else block when you need to display the edit-controls for the parameters of a specific subtype of PricingModelVisitor, you can simply call
somePricingModel.accept(new ParameterGuiVisitor(parentControl))
and it will populate the appropriate GUI for you.