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)
Related
I have a legacy C# library (a set of interrelated algorithms) in which there is a global god object which is passed to all classes. This god object (simply called Manager :D ) has a Parameters member, and an ObjectCollection member (among lots of others).
public class Manager
{
public Parameters {get; private set;}
public ObjectCollection {get; private set;}
...
...
}
I am unable to test the algorithms because everything takes the manager as dependency, and initializing that means I have to initialize everything. So I want to refactor this design.
Parameters has more than 100 fields in it, the values control the different algorithms. The ObjectCollection has the entities required for the overall execution of the engine, stored by Id, by Name, etc.
The following are the approaches I've though of, but not satisfied with:
Pass Parameters and ObjectCollection (or IParameters and IObjectCollection) instead of the Manager, but I don't think this solves any issue. I wouldn't know which of the parameters the algorithms would depend on.
Splitting the parameters class to smaller ones also is difficult as one parameter may affect many algorithms, so a logical separation is difficult. Plus the dependencies for each algorithm may end up to be many.
A singleton pattern like is usually done for a Logger, but that too is not testable.
Some of the parameters control the algorithm logic, some of the parameters are just required for the algorithm. I'm thinking of making each algorithm a separate class implementing an interface, and at the application start, deciding which algorithm to instantiate based on the parameter. I might end up splitting the current set of algorithm classes to many more, and I'm afraid I'll end up complicating it more and losing the structure of the algorithms.
Is there any standard way to deal with this, or is just splitting big classes to smaller ones and passing dependencies by constructor the only general advice?
In order to allow yourself to make small steps I'd start with a single algorithm and identify the parameters it requires. These can then be exposed in an interface so...
public interface IAmTheParametersForAlgorithm1
{
int OneThing {get;}
int AnotherThing {get;}
}
Then you can alter Manager so that it implements that interface and as in #marcel's answer expose those parameters directly on Manager.
Now you can test Algorithm1 with a very small mock or self-shunt because you don't need to initialise a gigantic Manager in order to run your test. And Algorithm1 no longer knows it takes a Manager object.
public Manager : IAmTheParametersForAlgorithm1 {}
public class Algorithm1
{
public Algorithm1(IAmTheParametersForAlgorithm1 parameters){}
}
Bit by bit you can continue expanding this to each of the sets of parameters and dealing with small, specific interfaces will allow you to identify where different algorithms have common parameters.
public Manager :
IAmTheParametersForAlgorithm1,
IAmTheParametersForAlgorithm2,
IAmTheParametersForAlgorithm3,
IAmTheParametersForAlgorithm4 {}
It also means that as you identify algorithms whose parameters are no longer accessed outside of their interface you can stop injecting Manager into those algorithms, take the parameters out of Manager, and create a new class which only provides those parameters.
This means you can keep your application running the whole time you're making this change if you aren't able to dedicate time to make one gigantic breaking change
For the Parameters, I would go with something like this:
public class Parameters
{
public int MyProperty1 { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
}
public class AlgorithmParameters1
{
private Parameters parameters;
public int MyProperty1 { get { return parameters.MyProperty1; } }
public int MyProperty3 { get { return parameters.MyProperty3; } }
public AlgorithmParameters1(Parameters parameters)
{
this.parameters = parameters;
}
}
public class Algorithm1
{
public void Run(AlgorithmParameters1 parameters)
{
//Access only MyProperty1 and MyProperty3...
}
}
Usage would look like:
var parameters = new Parameters()
{
MyProperty1 = 4,
MyProperty2 = 5,
MyProperty3 = 6,
};
new Algorithm1().Run(new AlgorithmParameters1(parameters));
By the way, I don't see how you could differ between parameters that control an algorithm and are required for it. By control do you mean they are used to make a decision which algorithm to take?
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.
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)
One of the most important aspects of OOP is data hiding. Can somebody explain using a simple piece of code what data hiding is exactly and why we need it?
Data or Information Hiding is a design principal proposed by David Paranas.
It says that you should hide the
design decisions in one part of the
program that are likely to be changed
from other parts of the program, there
by protecting the other parts from
being affected by the changes in the
first part.
Encapsulation is programming language feature which enables data hiding.
However note that you can do data\information hiding even without encapsulation. For example using modules or functions in non Object Oriented programming languages. Thus encapsulation is not data hiding but only a means of achieving it.
While doing encapsulation if you ignore the underlying principal then you will not have a good design. For example consider this class -
public class ActionHistory
{
private string[] _actionHistory;
public string[] HistoryItems
{
get{return _actionHistory; }
set{ _actionHistory = value; }
}
}
This calls encapsulates an array. But it does not hide the design decision of using a string[] as an internal storage. If we want to change the internal storage later on it will affect the code using this class as well.
Better design would be -
public class ActionHistory
{
private string[] _actionHistory;
public IEnumerable<string> HistoryItems
{
get{return _actionHistory; }
}
}
I'm guessing by data hiding you mean something like encapsulation or having a variable within an object and only exposing it by get and modify methods, usually when you want to enforce some logic to do with setting a value?
public class Customer
{
private decimal _accountBalance;
public decimal GetBalance()
{
return _accountBalance;
}
public void AddCharge(decimal charge)
{
_accountBalance += charge;
if (_accountBalance < 0)
{
throw new ArgumentException(
"The charge cannot put the customer in credit");
}
}
}
I.e. in this example, I'm allowing the consuming class to get the balance of the Customer, but I'm not allowing them to set it directly. However I've exposed a method that allows me to modify the _accountBalance within the class instance by adding to it via a charge in an AddCharge method.
Here's an article you may find useful.
Information hiding (or more accurately encapsulation) is the practice of restricting direct access to your information on a class. We use getters/setters or more advanced constructs in C# called properties.
This lets us govern how the data is accessed, so we can sanitize inputs and format outputs later if it's required.
The idea is on any public interface, we cannot trust the calling body to do the right thing, so if you make sure it can ONLY do the right thing, you'll have less problems.
Example:
public class InformationHiding
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
/// This example ensures you can't have a negative age
/// as this would probably mess up logic somewhere in
/// this class.
private int _age;
public int Age
{
get { return _age; }
set { if (value < 0) { _age = 0; } else { _age = value; } }
}
}
Imagine that the users of your class are trying to come up with ways to make your class no longer fulfill its contract. For instance, your Banking object may have a contract that ensures that all Transactions are recorded in a log. Suppose mutation of the Bank's TransactionLog were publically accessible; now a consuming class could initiate suspect transactions and modify the log to remove the records.
This is an extreme example, but the basic principles remain the same. It's up to the class author to maintain the contractual obligations of the class and this means you either need to have weak contractual obligations (reducing the usefulness of your class) or you need to be very careful about how your state can be mutated.
What is data hiding?
Here's an example:
public class Vehicle
{
private bool isEngineStarted;
private void StartEngine()
{
// Code here.
this.isEngineStarted = true;
}
public void GoToLocation(Location location)
{
if (!this.isEngineStarted)
{
this.StartEngine();
}
// Code here: move to a new location.
}
}
As you see, the isEngineStarted field is private, ie. accessible from the class itself. In fact, when calling an object of type Vehicle, we do need to move the vehicle to a location, but don't need to know how this will be done. For example, it doesn't matter, for the caller object, if the engine is started or not: if it's not, it's to the Vehicle object to start it before moving to a location.
Why do we need this?
Mostly to make the code easier to read and to use. Classes may have dozens or hundreds of fields and properties that are used only by them. Exposing all those fields and properties to the outside world will be confusing.
Another reason is that it is easier to control a state of a private field/property. For example, in the sample code above, imagine StartEngine is performing some tasks, then assigning true to this.isEngineStarted. If isEngineStarted is public, another class would be able to set it to true, without performing tasks made by StartEngine. In this case, the value of isEngineStarted will be unreliable.
Data Hiding is defined as hiding a base class method in a derived class by naming the new class method the same name as the base class method.
class Person
{
public string AnswerGreeting()
{
return "Hi, I'm doing well. And you?";
}
}
class Employee : Person
{
new public string AnswerGreeting()
{
"Hi, and welcome to our resort.";
}
}
In this c# code, the new keyword prevents the compiler from giving a warning that the base class implementation of AnswerGreeting is being hidden by the implementation of a method with the same name in the derived class. Also known as "data hiding by inheritance".
By data hiding you are presumably referring to encapsulation. Encapsulation is defined by wikipedia as follows:
Encapsulation conceals the functional
details of a class from objects that
send messages to it.
To explain a bit further, when you design a class you can design public and private members. The class exposes its public members to other code in the program, but only the code written in the class can access the private members.
In this way a class exposes a public interface but can hide the implementation of that interface, which can include hiding how the data that the class holds is implemented.
Here is an example of a simple mathematical angle class that exposes values for both degrees and radians, but the actual storage format of the data is hidden and can be changed in the future without breaking the rest of the program.
public class Angle
{
private double _angleInDegrees;
public double Degrees
{
get
{
return _angleInDegrees;
}
set
{
_angleInDegrees = value;
}
}
public double Radians
{
get
{
return _angleInDegrees * PI / 180;
}
set
{
_angleInDegrees = value * 180 / PI;
}
}
}
I'm working on modeling a business domain object in a class and am wondering what would be the best way to properly encapsulate private fields that only apply to a few methods.
When I started, my code originally looked like this:
public class DiscountEngine
{
public Cart As Cart { get; set;}
public Discount As Discount { get; set;}
public void ApplySKUGroupDiscountToCart()
{
...
}
}
However, ApplySKUGroupDiscountToCart() was starting to get ugly, so I decided to refactor the code into smaller private methods that get called from ApplySKUGroupDiscountToCart(). I started by passing in lots of local variables into the helper method, but then decided to pull out variables common to both routines and make them private modular variables. The new code looks like this:
public class DiscountEngine
{
public Cart As Cart { get; set;}
public Discount As Discount { get; set;}
private int _SKUGroupItemDiscountsApplied = 0
private int _SKUGroupTotalDiscounts = 0
private int _SKUGroupID = 0
public void ApplySKUGroupDiscountToCart()
{
...
}
private void ApplyDiscountToSingleCartItem(ref CartItem cartI,
ref DiscountItem discountI)
{
...
}
}
On the one hand, the three private integer fields are useful for allowing the related methods to share common variables without needing to pass them back and forth as parameters. However, these variables are only applicable to these related methods and any other methods I might add would have no need to see them.
Is there a way to encapsulate the private fields and their related methods while still remaining a part of the DiscountEngine class? Is there a better way altogether of dealing with this problem?
Normally, making a class field private implies "I have enough discipline to ensure that this field is only used in an appropriate manner inside this class". If your class is too big for you to say that with confidence, then maybe the class is trying to do too many different things, and should be split up (see SRP).
Anyway, enough of the theory :-). If you want to stick with one class then you could always encapsulate those three fields into a private nested class, e.g.
public class DiscountEngine
{
public Cart As Cart { get; set;}
public Discount As Discount { get; set;}
private class SKUGroup
{
public int ItemDiscountsApplied = 0
public int TotalDiscounts = 0
public int ID = 0
}
public void ApplySKUGroupDiscountToCart()
{
...
}
private void ApplyDiscountToSingleCartItem(ref CartItem cartI,
ref DiscountItem discountI)
{
...
}
}
That gives you a bit more freedom to pass instances of the class around your code as method parameters.
You could take this a step further, and move any private methods that act on the SKU data into the nested class as well.
First things first, you very likely don't need to pass the parameters to ApplyDiscountToSingleCartItem as ref. Short version: unless you're actually assigning a value to the variable that you want to be visible to the calling code, you don't need ref. Modifying variable and property values on them will be visible to the calling code without passing them as ref.
Second, there is no way to scope a variable in between instance and local, which is what you're asking. The only way to accomplish this would be to refactor this functionality into another class (likely a nested private class).
Don't, however, use instance variables as a way to pass data between functions. If the data becomes "stale" after the function is called, then it should be a parameter, not an instance variable.
I would say the only other way that I can think of to handle this would be to extract all the methods and private variables that are associated with them into a separate class. That way you keep all that encapsulated. But not sure if that would make sense in the context of your domain objects.
You could always create a nested (inner) class to bundle together parameters that have a common use. In this way you could still pass them to your private methods without having to pass around l.ots of arguments - you'd just pass an instance of the private type.
"these variables are only applicable to these related methods and any other methods I might add would have no need to see them."
First of all, keep in mind that one of the first rules of OO development is to build what the customer wants THEN apply OO design like basic OO rules and patterns. Your quote verges on saying you want to plan for the unknown. Be careful that the unknown is "more of the same" not NEW requirements. Otherwise, this class is going to end up becoming a God Object.
If you find you have many members that aren't used by the methods, then divide and conquer.