views:

472

answers:

8

more of original content deleted to make question easier to reference:

So I have a House class that has a method House.buy(Person p), causing the person to buy the house. I want to know if its possible for the Person to buy the House, so I also have a method House.tryBuy(Player p) that returns if the Person can buy the house. I have an enum BuyState with values like OK, NotEnoughMoney, and AlreadyOwned. There's a few different conditions to be satisfied, and the client would like to know which failed. But what if multiple conditions fail? I could either have a hierarchy, like if House is already owned and Person doesn't have enough money, return BuyStates.AlreadyOwned. But this only lets me tell the client one thing.

I could have N separate conditions and an enum with N*N values, like ConditionA_AND_ConditionB_ANDConditionC but that makes no sense at all for several reasons. I know there are bit-fields, with a bit for each condition, but they just seem too low-level, annoying to implement, and not-scalable. So I need a way to return a list of values from an enum, So how about a class like this:

class C<type_of_enum> {
    private List<type_of_enum> values;

    //etc etc
}

Is this the "best" possible design?

(keeping this question about java AND C# to keep answers valid)

A: 

It sounds fine to me. You want to return a list of the conditions that failed, and you're returning a list of the conditions that failed.

mquander
+1  A: 

Yes, that does seem like the best design. You want to return a list (or set) of reasons, so it's natural to return it as a set.

sleske
+1  A: 

I think returning a list of reasons not to buy is great; it's very expressive of what you're trying to do. A set would probably be more appropriate, but only slightly so.

Carl Manaster
+6  A: 

In C# I would go for a flags enumeration.

Check Designing Flags Enumerations.

João Angelo
+9  A: 

In Java, the most natural way to do this is with EnumSet. An example of constructing one:

return EnumSet.of(BuyConditions.NotEnoughMoney, BuyConditions.AlreadyOwned);
Michael Myers
+1. This is the right solution for Java. In particular, as `EnumSet` s are actually nothing else but bit-fields with a more friendly (`Set` like) interface
Dirk
A: 

You could use a callback:

class House {

    public void buy(Result result) {

        if (true)
            result.ok(this);
        else
            result.error(this, EnumSet.of(Status.NOT_ENOUGH_MONEY, Status.ALREADY_OWNED));

    }

}

enum Status {

    NOT_ENOUGH_MONEY,
    ALREADY_OWNED

}

interface Result {

    public void ok(House house);

    public void error(House house, EnumSet<Status> status);

}
yawn
A: 

Instead of returning a list, you can pass a list pointer to the function, to be filled up with reasons in case error conditions arise. The function itself can return 0 to indicate success and 1 for failure, in which case you can check the content of the list.

This will make it faster to know if the function call was successful or not, assuming most of the time it will be success.

Joy Dutta
+7  A: 

Is this the "best" possible design?

This seems like a bizarre design all around. When modeling real-world things in software, it pays dividends to make the model reflect reality; such models are easier to understand, maintain, and expand.

First off, a house is not something that buys a person. A person is something that buys a house. The "buy" method should be on "person", not on "house".

Second, a house is not something which determines whether a house can be bought. The owner of the house is the entity which determines whether it can be bought. (Why is there an "already owned" error condition? Of course the house is already owned. Someone owns it.)

Third, you might have to consider what happens in a world where multiple buyers might be attempting to buy the house all at once. In reality, the seller collects various offers and makes counteroffers, sales may be contingent upon other events, and so on. Should all of those things be present in the model? If so, where? Probably in the state of the object representing the owner, since the owner is the thing being negotiated with.

Fourth, in reality, house purchasing transactions usually involve a trusted third party to do escrow, and various other parties such as the seller's and buyer's lenders who might be providing funds or holding liens. Are those parties reflected in this model?

Fifth, if your intention is to add to your model "reasons why you cannot buy this house", then what you are describing is a policy system. In that case, represent policies as first-class objects in your system, so that they can be manipulated like any other objects. Owners have policies about under what conditions they will sell. Banks have policies about the conditions under which they will lend. And so on.

In this model, your problem becomes "ask the policy resolution engine if the buyer meets all the necessary conditions imposed by every relevant agency's policy trees in order to purchase a given house". In other words, the question "can X buy Y?" is not for either X or Y to figure out; it's a matter for a policy resolution engine to work out, and THAT thing is what gives you back a list of the policies X has failed to fulfill in order to purchase Y from Z.

Make sense?

Eric Lippert