Bit of a brain teaser: Custom logic strings with C# classes - c#

So, my basic set up is like so: I have items, which are restricted to different classes. These items have effects, which are also restricted to different classes. For example, I might have an item that may only be wielded by elves, while another item might be wielded by everyone, but gives specific bonuses/effects to elves.
Here's a Restriction class:
public class Restriction {
private int _base_id = 0;
private bool _qualify = true;
public Restriction() { }
// ... Base_ID and Qualify getters and setters here
public virtual bool Check(int c) {
if(_qualify) { return c == _base_id; }
else { return c != _base_id; }
}
A child of the Restriction class might be RaceRestriction, which only overrides the constructor:
public RaceRestriction(reference.races r, bool qual) {
Base_ID = (int)r; Qualify = qual;
}
reference.races r is an enum in a reference file. The idea here is that I can extend this "Restriction" syntax to any class that I define in the reference file -- so I can make Restrictions on race, class, stats, whatever I need.
So, this all culminates later, when I define (for example) an item, which has restrictions on who can equip it.
Below is a snippet from the Equipment class, where I define a piece of equipment for later use (hopefully it's readable as is):
public Equipment() {
...
_master_equipment_list[1] = new Equipment {
Name = "Sword",
Description = "It's just a sword for demonstration",
Stats = {
new Attribute {
Stat_Modifier = new KeyValuePair<reference.stats, int>(reference.stats.ATTACK, 5),
Restrictions = {
new RaceRestriction(reference.races.TROLL, false)
}
}
},
Restrictions = {
new ClassRestriction(reference.class.WARRIOR, true)
}
}
So the idea behind this is that using this system, I've defined a sword that can only be used by warriors (base warrior true restriction on the item), and it gives 5 attack to any trolls wielding it.
What I've cornered myself into is that this will only work for either logical AND or logical OR strings of thought. Say my item says "warriors can use this" and it says "elves can use this." Do I really mean "warriors or elves" or do I mean "warrior elves?"
That distinction, I think, is going to be necessary -- so I need to attach some logic to each restriction and make, essentially, I think, sets of restrictions that are tied to one another, that string with other sets of restrictions, etc., but I feel like that will get out of hand very fast.
Is there a better way I can do this?

Rather than defining specific restriction classes, I would design this by defining an interface called IRestrictable to be implemented by the Equipment classes. This interface would contain at least one method called CheckEligibility (or similar) which would return a bool. Your equipment class would then be free to use whatever logic expression it liked to come up with the answer, based on whatever inputs you wanted and whatever information the class had available at the time. You could have several methods on the interface if you need to check restrictions under different circumstances. You would be free to implement specific classes deriving from Equipment for specific types of equipment that had complicated rules.

Related

How can factory's have access to other factory's products in abstract factory pattern

In this example for the NYPizzaIngredientFactory, they can only make pizza with ThinCrustDough. How can i make a pizza that could use another factory's ingredients like ThickCrustDough from ChicagoPizzaIngredientFactory. I want to try stay away from builder and stick with abstract factory patterns and factory methods.
Your NYPizzaStore would have to use the ChicagoPizzaIngredientFactory if you want it to be able to use ThickCrustDough.
If you think about the practicality of this, however, it probably doesn't make sense to have them ship you the ingredients from Chicago.
In my mind, you have two options:
Have another factory located in NY that can produce thick dough (e.g. NYThickPizzaIngredientFactory). This is because your interface has a single createDough method that takes no arguments so you can't tell it what type of dough to make. It can only make one.
Alter your interface so that the createDough method accepts arguments that can tell the factory what type of dough to create. This is the one I would recommend.
The type of arguments can also be based on the particular factory. For instance:
//TDoughArts tells you what type of arguments the factory needs in order to make dough.
public interface IPizzaIngredientFactory<TDoughArgs> where TDoughArgs : IDoughArgs
{
//....
IDough CreateDough(TDoughArgs doughArgs);
//....
}
public interface IDoughArgs
{
}
public class NYPizzaDoughArgs : IDoughArgs
{
public enum DoughTypes
{
Thin = 0,
Thick = 1
}
public DoughTypes DoughType { get; set; }
}
public class NYPizzaIngredientFactory : IPizzaIngredientFactory<NYPizzaDoughArgs>
{
//....
public IDough CreateDough(NYPizzaDoughArgs doughArgs)
{
//Make the right dough based on args here
if(doughArgs.DoughType == DoughTypes.Thin)
//...
}
//....
}
I whipped this out in a few minutes so check for consistency, but I think you will get the idea.
You don't have to use generics. You can simply stick with the IDoughArgs interface if you don't want more specificity.
Usage:
var factory = new NYPizzaIngredientFactory();
var args = new NYPizzaDoughArgs();
args.DoughType = NYPizzaDoughArgs.DoughTypes.Thick;
var dough = factory.createDough(args);
The first problem I see is this:
public interface IDoughArgs
{
}
public class NYPizzaDoughArgs : IDoughArgs
{
public enum DoughTypes
{
Thin = 0,
Thick = 1
}
public DoughTypes DoughType { get; set; }
}
IDoughArgs has no members. The class that implements it, NYPizzaDoughArgs, has properties which are not implementations of IDoughArgs. That renders the IDoughArgs interface meaningless.
Additionally, look at this class declaration:
public class NYPizzaIngredientFactory : IPizzaIngredientFactory<NYPizzaDoughArgs>
What class is going to "know" the generic argument and know to create this class as opposed to some other generic implementation? It's going to get confusing when you get to that part. You'll need some sort of factory to create your factory.
Then, if you decide that ingredient factories vary by more than just the type of dough, and you need more generic arguments, it's going to get really messy.
And, what happens if, in addition to having options such as thickness that are specific to just one dough type, you need options that are specific to just one thickness? Perhaps thick dough is only an option if you've selected New York or Chicago style (not European) and stuffed crust is only an option if you've selected a thick crust. That's going to get really difficult to describe with interfaces. It sounds more like data.
Here's a stab at another way to implement this:
public enum PizzaStyle
{
NewYork = 1,
Chicago = 2,
Greek = 4
}
public enum CrustType
{
Thick = 1024,
Thin = 2048,
HandTossed = 4096
}
public enum CrustOption
{
Stuffed = 32768
}
public enum PizzaDoughOption
{
NewYorkThin = PizzaStyle.NewYork + CrustType.Thin,
NewYorkHandTossed = PizzaStyle.NewYork + CrustType.HandTossed,
NewYorkThick = PizzaStyle.NewYork + CrustType.Thick,
NewYorkThickStuffed = NewYorkThick + CrustOption.Stuffed,
ChicagoThin = PizzaStyle.Chicago + CrustType.Thin,
ChicagoHandTossed = PizzaStyle.Chicago + CrustType.HandTossed,
ChicagoThick = PizzaStyle.Chicago + CrustType.Thick,
ChicagoThickStuffed = ChicagoThick + CrustOption.Stuffed,
Greek = PizzaStyle.Greek // only comes one way?
}
There are other ways to represent this same data. Even if there were fifty values in the PizzaDoughOption enumeration, it's probably still easier that way, building a definitive, readable list of valid options, as opposed to trying to represent that in code with a bunch of branches. (If you want to unit test that, you'll end up coding every single combination anyway in unit tests.)
And there are several ways you could use this data. You could present just a big list of options. You could allow users to select from the various options and, as you go, determine whether it matches a valid combination. Or they could select any option and you could narrow the list of options according to which include the desired option. (You want a stuffed crust? Ok, that's either New York thick crust or Chicago thick crust.)
Now, if you need a factory to create dough according to type, you could do this:
public interface IDoughFactory
{
Dough GetDough(PizzaDoughOption doughOption);
}
The implementation might look something like this. To be honest I might use a "factory factory" here, but for now since there are only three types I'll keep it simpler.
public class DoughFactory : IDoughFactory
{
// Each of these also implement IDoughFactory
private readonly NewYorkDoughFactory _newYorkDoughFactory;
private readonly ChicagoDoughFactory _chicagoDoughFactory;
private readonly GreekDoughFactory _greekDoughFactory;
public DoughFactory(
NewYorkDoughFactory newYorkDoughFactory,
ChicagoDoughFactory chicagoDoughFactory,
GreekDoughFactory greekDoughFactory)
{
_newYorkDoughFactory = newYorkDoughFactory;
_chicagoDoughFactory = chicagoDoughFactory;
_greekDoughFactory = greekDoughFactory;
}
public Dough GetDough(PizzaDoughOption doughOption)
{
if (MatchesPizzaStyle(doughOption, PizzaStyle.NewYork))
return _newYorkDoughFactory.GetDough(doughOption);
if (MatchesPizzaStyle(doughOption, PizzaStyle.Chicago))
return _chicagoDoughFactory.GetDough(doughOption);
if (MatchesPizzaStyle(doughOption, PizzaStyle.Greek))
return _greekDoughFactory.GetDough(doughOption);
// Throw an exception or return a default dough type. I'd throw the exception.
}
private bool MatchesPizzaStyle(PizzaDoughOption doughOption, PizzaStyle pizzaStyle)
{
return ((int) doughOptions & (int) pizzaStyle) == (int) pizzaStyle;
}
}
Now your more concrete dough factories (New York, Chicago, Greek) all receive the same PizzaDoughOption. If they care whether thin or thick has been selected, they can handle it. If that option doesn't exist they can ignore it. Even if something has gone wrong in an outer class and somehow someone has invoked GreekDoughFactory with the StuffedCrust option, it won't fail. It just ignores it.
What would be the possible point to all of this?
First, the class creating a pizza has no knowledge of the intricacies of creating the right dough type. It just depends on a dough factory, passes a parameter, and gets the right dough. That's simple and testable.
Second, you don't have to call new anywhere. You can employ dependency injection all the way down. That way the class that depends on the abstract IDoughFactory doesn't know anything about what dependencies DoughFactory has.
Likewise, maybe the concrete dough factories have dependencies of their own and they differ significantly from one to the next. As long as those are getting resolved from the container and injected into DoughFactory, that's fine, and DoughFactory won't know anything about their dependencies.
All of the dependencies are wired up in your DI container, but the classes themselves are small, simple, and testable, depending on abstractions and not coupled to implementations of anything.
Someone might look and this and think it's a little more complicated. What's critical is that not only does it keep individual classes decoupled, but it leaves a path forward for future change. The design of your classes, which shouldn't have to change too much, won't closely mirror the details of specific types of pizzas, which can and should change. You don't want to have to re-architect your pizza application because of a new kind of pizza.

Check type with boolean property?

I'm very aware of type checking, but have found myself in a unique situation and I'm beginning to question whether I'm within best practices. Hopefully the veteran comments will give me some direction and things worth thinking more deeply about. And, to be honest, it's not that what I have will not work, but as I'm making some other changes I'm wondering what the pitfalls might be and whether I should change tactics. There doesn't seem to be a lot out there (in fact I havent' seen anything as basic type checking takes the majority of the search results).
I have a situation where I'm developing a bill of material interface system. In this system, the following class diagram applies:
This is generically speaking, but the point here is that there are only three concrete types worth concerning. Because it's easy to set property values in the constructors of the objects, I had defined (generically speaking again of course) the IMaterial interface as so:
public interface IMaterial
{
bool IsCommodity { get; }
bool IsAssembly { get; }
bool IsUnclassified { get; }
...
}
Originally, the thought was that the object graph has very little room to change, performance is improved through a preset boolean value, and I don't have to worry about breaking various other principles by type checking concrete types.
So for example, I can do this...
bool hasCommodities = materialCollection.Any(item => item.IsCommodity);
bool hasAssemblies = materialCollection.Any(item => item.IsAssembly);
bool hasDescriptionOnly = materialCollection.Any(item => item.IsUnclassified);
or this...
if (bomMaterial.IsAssembly)
{
symbol = new BomAssemblySymbol();
}
else
{
symbol = new BomItemSymbol();
}
instead of this...
bool hasCommodities = materialCollection.Any(item => item is ClassifiedItem);
bool hasAssemblies = materialCollection.Any(item => item is Assembly);
bool hasDescriptionOnly = materialCollection.Any(item => item is UnclassifiedItem);
or this...
if (bomMaterial is Assembly)
{
symbol = new BomAssemblySymbol();
}
else
{
symbol = new BomItemSymbol();
}
So in my case, the interface's use of properties means less dependency on concrete types in the implementation detail. But then again, it begs the question, what if another type does come along? What's the best answer here? And is there a pattern that maybe I'm overlooking and should be considering for this? And if anyone is wondering why the consuming code cares, it's because with the CAD system, there is a single command the user interacts with that in turn leverages these objects. I can't create separate commands for them just because of the single line of code difference.
Update
Here's a more complete example showing how the CAD-side seems to bottle neck processes. The TryGetMaterialInformation() method prompts the user in the CAD system for specific input. The SymbolUtility.InsertSymbol() method just wraps a common set of user prompts for inserting any symbol and then inserts it.
public override void Execute()
{
IMaterial bomMaterial = null;
bool multipleByReference = false;
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
if (!TryGetMaterialInformation(out bomMaterial, out multipleByReference))
{
ed.WriteMessage("\nExiting command.\n");
return;
}
IBlockSymbol symbol;
if (bomMaterial.IsAssembly)
{
symbol = new BomAssemblySymbol();
}
else
{
symbol = new BomItemSymbol();
}
if (multipleByReference)
{
SymbolUtility.InsertMultipleByReferenceSymbol(symbol, bomMaterial);
}
else
{
SymbolUtility.InsertSymbol(symbol, bomMaterial);
}
}
From SymbolUtility
internal static void InsertSymbol(IBlockSymbol symbol, IMaterial material)
{
ICADDocumentDTO document = new CADDocumentDTO();
Editor ed = document.ActiveDocument.Editor;
//Get the insert point
Point3d insertPoint = Point3d.Origin;
if (!CommandUtility.TryGetPoint("Select insert point: ", out insertPoint))
{
ed.WriteMessage("\nExiting command.\n");
return;
}
//Insert the object
using (ISystemDocumentLock documentLock = document.Lock())
{
CreateSymbolDefinition(symbol, document);
symbol.Insert(insertPoint, material, document);
}
}
If you have properties like IsCommodity, IsAssembly, and IsClassified, they should describe some sort of logical property that can be ascribed to an instance. They should not tell the consumer what the concrete type is.
The reason is that a consumer of IMaterial should neither know nor need to know about any concrete type that implements IMaterial.
If those properties actually indicate the concrete types, then all those properties accomplish is type checking, and they will lead to casting objects back to their concrete types, which defeats the purpose of creating an abstraction (interface.)
It looks that way to me since you're considering the properties as a direct alternative to type checking.
The alternative is that instead of the consumer looking at the class properties and deciding what to do or not to with the class, the consumer just tells the class what do to (calling a method) and the implementation of the class itself determines how to carry that out.

C# LSP Constructor Parameters and Guard Clauses

I've been reading about the Liskov Substitution Principle (LSP) and I'm a little confused on how you adhere to it correctly. Especially when interfaces and subclasses are being used.
For example, if I have a base class:
public abstract class AccountBase
{
private string primaryAccountHolder;
public string PrimaryAccountHolder
{
get { return this.primaryAccountHolder; }
set
{
if (value == null) throw ArgumentNullException("value");
this.primaryAccountHolder = value;
}
}
public string SecondaryAccountHolder { get; set; }
protected AccountBase(string primary)
{
if (primary == null) throw new ArgumentNullException("primary");
this.primaryAccountHolder = primary;
}
}
Now let's say I have two accounts that inherit from the base class. One that REQUIRES the SecondaryAccountHolder. Adding a null guard to the sub-class is a violation of LSP, correct? So how would I design my classes in such a way that they don't violate LSP but one of my sub-classes requires a secondary account holder and one does not?
Compound the question with the fact that there could be tons of different types of accounts and they'll need to be generated through a factory or factory that returns a builder or something.
And I have the same question with interfaces. If I have an interface:
public interface IPrintsSomething
{
void PrintSomething(string text);
}
Wouldn't it be a violation of LSP to add a null guard clause for text on any class that implements IPrintsSomething? How do you protect your invariants? That is the correct word right? :p
You should research tell-don't-ask, and command/query separation, you could start here: https://pragprog.com/articles/tell-dont-ask
You should endeavor to tell objects what you want them to do; do not ask them questions about their state, make a decision, and then tell them what to do.
There's always something you want to do with the properties, well don't ask the object for them tell it to do something with them.
Instead of asking it and making decisions like this:
string holders = account.PrimaryAccountHolder;
if (accountHolder.SecondaryAccountHolder != null)
{
holders += " " + accountHolder.SecondaryAccountHolder;
}
Tell it:
string holders = account.ListAllHoldersAsAString();
Ideally, you'd actually tell it what you actually want to do with that string:
account.MailMergeAllAccountHoldersNames(letterDocument);
Now the logic for dealing with two account holders is in the subclass. Could be one, two or n account holders, the calling code doesn't care or need to know.
As for LSP, well if there's a formally (or informally) documented contract that says the clients must check for null on the second holder from the start then that's fine. It's not nice, but any null-pointer-exceptions will be the client's fault for not using the class correctly. (Note it's not true that adding a boolean property improves upon this, it's just maybe a little more readable, i.e. does anyone check IList.IsReadOnly before writing to it?!).
However, if you started with the double holder account and then added that condition that the second account holder can be null later for the single account, then you changed the contract, and an instance of the single could break existing code. If you're in full control of all places that use accounts, then you're allowed to do that, if that's a public api
you're changing, that's a different matter.
But tell-don't-ask avoids the whole problem in this case.
So how would I design my classes in such a way that they don't violate LSP but one of my sub-classes requires a secondary account holder and one does not?
The way out of this problem is by surfacing this variability to the contract of the base class. It may look like this (unnecessary implementation details left out):
public abstract class AccountBase
{
public string PrimaryAccountHolder
{
get { … }
set { … }
}
public string SecondaryAccountHolder
{
get { … }
set
{
…
if (RequiresSecondaryAccountHolder && value == null) throw …;
…
}
}
public abstract bool RequiresSecondaryAccountHolder { get; }
}
Then you are not violating the LSP, because the user of AccountBase can determine whether they have to or have not to provide the value of SecondaryAcccountHolder.
And I have the same question with interfaces. … Wouldn't it be a violation of LSP to add a null guard clause for text on any class that implements IPrintsSomething?
Make the validation an obvious part of the interface's contract. How? Document, that the implementor must chek the value of text for null.

Static member access

It is likely that I am going about this all wrong, but I have a user control called CategoryControl, there can be many like it, for that reason I decided that many of its functions would be better served as static methods. I wanted to know if there is a "better" way of accessing these methods then passing an instance all over the class. The methods are public static as they will be updated by other methods. The though of making extension methods comes to mind..?
public CategoryControl(UserCategory userCategory)
{
InitializeComponent();
PopulateControl(userCategory, this);
}
private static void PopulateControl(UserCategory userCategory, CategoryControl instance)
{
SetCategoryTitle(userCategory, instance);
SetPercentCorrect(userCategory, instance);
SetQuestionsMissed(userCategory, instance);
SetBackgroundBar(userCategory, instance);
SetForegroundBar(userCategory, instance);
}
Updated::
The longer story is that I have a Panel on the screen, the panel contains relevant user categories. By relevant I mean that the user has the option of changing courses thus displaying a new set of categories. A user can also change the values of a category based on their interaction with the software. So...
A panel shows the categories of a course.
I maintain a list of the active Category Controls in the panel, and the main form tells the panel when to draw a new set of categories.
public void InitializeProgressPanel(UserCategories parentCategories)
{
Contract.Requires(parentCategories != null, "parentCategories is null.");
RemoveAllControlsFromList(_categoryControls);
UserCategories sortedUserCategories = parentCategories.SortByWorst();
int categoriesCount = parentCategories.Count();
int spacer = (Height - (CategoryControl.Controls_Height * categoriesCount)) / categoriesCount+1;
for (int i = 0; i < sortedUserCategories.Count; i++)
{
CategoryControl cc = new CategoryControl((UserCategory)sortedUserCategories[i]);
cc.Left = 0;
if (i == 0)
cc.Top = spacer;
else
cc.Top = (Controls[i - 1].Bottom + spacer);
Controls.Add(cc);
_categoryControls.Add(cc);
}
}
I would certainly not make extension methods if I had a class in hand that I could extend. Remember, the purpose of extension methods is to extend types that you cannot extend yourself.
The question at hand then is, should you say:
class C
{
public void Foo() { ... }
}
or
class C
{
public static void Foo(C c) { ... }
}
I would ask some questions like:
Is the class ever going to be subclassed? If so, should this be a virtual method?
Is Foo the kind of thing that an instance does to itself, or the sort of thing that it has done to it? An animal eats on its own, but an animal is fed by someone else.
UPDATE:
Some more questions I'd ask myself:
Are the properties and whatnot you are setting ever going to change? The less mutability you have in a class, the easier it is to test, the easier it is to reason about, and the fewer bugs you'll have. If the properties and whatnot are never going to change then do not set them in any kind of method. Set them in the constructor and then never worry about them again; they're correct.
Why not make them instance members, and do it like this
private UserCategory _userCategory;
public CategoryControl(UserCategory userCategory)
{
InitializeComponent();
this._userCategory = userCategory;
this.PopulateControl();
}
private void PopulateControl()
{
// to see userCategory you'd do "this._userCategory"
// to see the specific instance you could simply do "this"
SetCategoryTitle();
SetPercentCorrect();
SetQuestionsMissed();
SetBackgroundBar();
SetForegroundBar();
}
Seems better to have the functionality on one of the two classes involved in the interaction, rather than on some third party.
Here are two ways that spring to mind:
CategoryControl could have a public function PopulateCategory(UserCategory userCat)
UserCategory could have a public function PopulateFromControl(CategoryControl ctrl)
If all those operations about title and percent etc need to be separate actions, you'd just follow the model above but have separate functions for each item.
Just a shot in the dark here, but I'd probably try for something more like this:
private void PopulateControl(UserCategory userCategory)
{
CategoryTitle = GetCategoryTitle(userCategory);
PercentCorrect = GetPercentCorrect(userCategory);
...
}
Some questions may help...(?)
What benefit do you perceive in making the methods static? Converting the method to static, you are taking away the implicit passing of "this", and passing it in manually every time. How does that help? (It won't make the code any more efficient, it just means you have to pass 'instance' into every call you make, so you need to write more code)
Does the user category change a lot? If not, rather than passing it in for every call, would it make more sense to make it a member variable?
Would you really want to call all these static methods one by one to change all the different parameters of the control? Look at how the client will use this class and you may find that you can roll all of those options into one or two methods that take a bunch of parameters and apply them all in one hit. (Often if you want to change one setting, you will want to change several settings together)

Architecture for Game - To couple or not to couple?

I'm building a simple game which consists of Mobiles -- the in-game characters (Mobs). Each mob can perform certain functions. In order to give that functionality to the Mob, I've created a Behavior.
For example, let's say a mob needs to move around the game field, I would give it the MoveBehavior - this is added to an internal list of Behaviors for the mob class:
// Defined in the Mob class
List<Behavior> behaviors;
// Later on, add behavior...
movingMob.addBehavior(new MovingBehavior());
My question is this. Most behaviors will manipulate something about the mob. In the MoveBehavior example, it will change the mob's X,Y position in the world. However, each behavior needs specific information, such as "movementRate" -- where should movementRate be stored?
Should it be stored in the Mob class? Other Mobs may attempt to interact with it by slowing/speeding up the mob and it's easier to access at the mob level... but not all mobs have a movementRate so it would cause clutter.
Or should it be stored in the MoveBehavior class? This hides it away, making it a little harder for other mobs to interact with - but it doesn't clutter up a non-moving mob with extra and un-used properties (for example, a tower that doesn't move would never need to use the movementRate).
This is the classic "behavioral composition" problem. The trade-off is that the more independent the behaviors are, the more difficult it is for them to interact with each other.
From a game programming viewpoint, the simplest solution is a decide on a set of "core" or "engine" data, and put that in the main object, then have the behaviors be able to access and modify that data - potentially through a functional interface.
If you want behavior specific data, that's fine, but to avoid collisions in the names of variables, you may want to make the interface for accessing it include the behavior name. Like:
obj.getBehaviorValue("movement", "speed")
obj.setBehaviorValue("movement", "speed", 4)
That way two behaviors can both define their own variables called speed and not collide. This type of cross-behavior getters and setters would allow communication when it is required.
I'd suggest looking at a scripting language like Lua or Python for this..
You could borrow a pattern from WPF (attached properties). The WPF guys needed a way to sort of attach properties to controls at run time. (for example, if you put a control inside a grid, it would be nice for the control to have a Row property -- they pseudo did this with attached properties.
It works something like: (note this probably doesn't precisely match WPF's implementation, and I'm leaving out the dependency property registration, as you aren't using XAML)
public class MoveBehavior: Behavior
{
private static Dictionary<Mob, int> MovementRateProperty;
public static void SetMovementRate(Mob theMob, int theRate)
{
MovementRateProperty[theMob] = theRate;
}
public static int GetMovementRate(Mob theMob)
{
// note, you will need handling for cases where it doesn't exist, etc
return MovementRateProperty[theMob];
}
}
The thing here is that the Behavior owns the property, but you don't have to go spelunking to get it Here's some code that retrieves a mob's movement rate:
// retrieve the rate for a given mob
int rate = MoveBehavior.GetMovementRate(theMob);
// set the rate for a given mob
MoveBehavior.SetMovementRate(mob, 5);
If it is related to the behavior, and only makes sense in the context of that behavior, then it should be stored as part of it.
A movement rate only makes sense for something that can move. Which means it should be stored as part of the object that represents its ability to move, which seems to be your MoveBehavior.
If that makes it too hard to access, it sounds more like a problem with your design. Then the question is not "should I cheat, and place some of the variables inside the Mob class instead of the behavior it belongs to", but rather "how do I make it easier to interact with these individual behaviors".
I can think of several ways to implement this. The obvious is a simple member function on the Mob class which allows you to select individual behaviors, something like this:
class Mob {
private List<Behavior> behaviors;
public T Get<T>(); // try to find the requested behavior type, and return it if it exists
}
Others can then do something like this:
Mob m;
MovementBehavior b = m.Get<MovementBehavior();
if (b != null) {
b.SetMovementRate(1.20f);
}
You might also place some of this outside the Mob class, creating a helper function which modifies the movement rate if it exists, and does nothing otherwise:
static class MovementHelper {
public static SetMovementRate(Mob m, float movementrate){
MovementBehavior b = m.Get<MovementBehavior();
if (b != null) {
b.SetMovementRate(1.20f);
}
}
}
and then others could use it like this:
MovementHelper.SetMovementRate(m, 1.20f);
which would provide easy access to modifying the behavior, but without cluttering up the Mob class. (Of course, it'd be tempting to turn this into an extension method, but that might lead to too much assorted clutter in the Mob class' public interface. It may be preferable to make it clear that this is helper functionality that resides outside the Mob class itself)
Take a look at component systems/entity systems design:
http://www.devmaster.net/articles/oo-game-design/
By far the best I've seen till now.
Smart people say it's the only way to go with larger games, but it requires a shift in how you think about OOP.
So what are you trying to do?
What's the simplest way for you to store the movement rate data?
If it is only needed in the MoveBehavior class then it should be in there:
public class MoveBehavior {
public int MovementRate { get; set; }
}
If it is needed inherently by the Mob class then it will be easier exposed through the Mob class:
public class Mob {
public int MovementRate { get; set; }
}
public class MoveBehavior {
public MoveBehavior(Mob mob) { MobInEffect = mob; }
public Mob MobInEffect {get; set;}
// can access MovementRate through MovInEffect.MovementRate
}
So it all depends on what you're trying to achieve with this behavior logic. I'd recommend you push the design decision until you really need to do it one way or another. Concentrate on doing it simple first and refactor later. Usually more often than not, doing early design guesswork can lead to overcomplicated architecture.
A more pragmatic solution…
What I mean is that you implement whatever you wanted from movement the in the Mob class first:
public class Mob {
// Constructors and stuff here
public void Move(long ticks)
{
// do some voodoo magic with movement and MovementRate here
}
protected int MovementRate { get; set; }
}
And when that works, rip out that implementation to a MoveBehavior class if you really need to:
public class Mob {
// Constructors and stuff here
public MoveBehavior Moving { set; get; }
public void Move(long ticks)
{
Moving.Move(ticks, this);
}
}
public class MoveBehavior {
protected int MovementRate { get; set; }
public void Move(long ticks, Mob mob)
{
// logic moved over here now
}
}
After that if you really need to do more than one type of behavior but they share a common interface then create that interface by then and let the behaviors implement that.
Edit: The below answer only really makes sense if you're not instancing a new MovingBehavior for every mob, but just have a singleton MovingBehavior.
I'd say that the mob (ugh, I hate that word for game NPCs, but it's not your fault) should, when addBehavior() is called, get a BehaviorState object that's returned from addBehavior() and that it keeps around, and is keyed to the behavior added. Then provide an interface for MovingBehavior to easily retrieve its BehaviorState object from movingMob, and it stores whatever it needs to store there.
If i was designing something like this i would try out using interfaces to define which behaviors a mob has:
public interface IMovable
{
int MovementRate { get; set; }
void MoveTo(int x, int y);
}
public class Monster : Mob, IMovable
{
public int MovementRate { get; set; }
public void MoveTo(int x, int y)
{
// ...
}
}
This way you can check if a mob can move by doing something like this:
Monster m = new Monster();
if (m is IMovable)
{
m.MoveTo(someX, someY);
}
IMHO, the movement rate is associated with the movingBehavior rather than with a Mob itself, and as you said, it doesn't necessarily move. So the variable should be associated with the behavior, a change in the movementRate is a change to his Behavior, not to the mob himself.
You could also create a base Mob class, and derive a MovingMob one. But I guess, this doesn't really apply, once apparently you can have an arbitrary combination of different behaviors...
-- EDIT --
First, apparently you won't have the same type of behavior twice in the same Mob (like, no mob has two movementBehaviors at the same type), so a set is a better option in this case, as it avoids duplicates
You could have a method in each mob like
public Behavior GetBehavior(Type type)
{
foreach (var behavior in behaviorHashSet)
{
if ( behavior.GetType() == type)
return behavior;
}
return null;
}
Then you could do whatever you want with this behavior once you have a Mob. Also, you could change the GetHashCode() and Equals() method to ensure you have no duplicate, or make the GetBehavior method even faster (constant time)

Categories

Resources