Avoiding transposed parameters without creating coupling - c#

I'm currently building a test application that manages parts for engineers, and I've hit a snag. I have a few different classes, including PartsModel and EngineerModel, and I want to update a list of parts that an engineer has, but I'm mindful of issues from either transposed parameters or from structuring the code in a way that unnecessarily couples to a particular class.
The two classes, with some relevant properties:
public class PartModel
{
public int PartId { get; private set; }
public string PartTitle { get; set; }
public string PartDescription { get; set; }
public int Quantity { get; set; }
public int MinimumStock { get; set; }
public void AddToStock (int quantityToAdd) {
Quantity += quantityToAdd;
}
public void RemoveFromStock (int quantityToRemove) {
Quantity -= quantityToRemove;
CheckMinimumStock();
}
}
public class EngineerModel
{
public int EngineerId { get; private set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<PartModel> PartsInStock { get; set; } = Factory.CreatePartsList();
}
As you can see, each engineer has a list of parts they have in stock via a List<PartModel>. I want to pass another list to this one so that I can update it respectively (incrementing or decrementing quantities, and then adding or removing parts to the list as necessary).
The first warning bell is that it takes two inputs of the same type, and is going to fill one from the other one (which isn't needed afterwards), so you're essentially modifying one input and destroying the other. To me, this presents a danger of the inputs getting transposed and the wrong list being either returned or updated (depending on whether it returns or just acts on the list). Because it removes items that have no quantity, it can't check the list length and just update the longer one, because there are possible cases where the engineer's list is shorter (maybe they're a new engineer, or maybe they just had a large shipment of parts sent when they were running low on stock). If it did just keep parts with quantity zero, then you're threatening scalability of both engineers and parts (not to mention any other objects that use the same operation).
So, put it as a method in the EngineerModel class and operate on PartsInStock, right? But what about when I want to use the same operation on other classes (e.g. if I have a list of parts associated to a work task)? Then I extract the method out to another class and... I'm passing the two lists as parameters in the method, so I'm back to where I was.
Am I being reasonable in not wanting to have two parameters of the same type, and how do I structure the code to deal with this, but without creating unnecessary coupling? If I'm not being reasonable, what am I overlooking?

Use an extension method
Thanks to #DavidBrowne-Microsoft for clarifying this. By defining an extension method for List<PartModel>, it only needs the one parameter - the list containing the updates (foreach below based on #Valentin's answer to this question).
public static class PartsHandler
{
public static List<PartModel> UpdateStockQuantitiesWith(this List<PartModel> stockToBeUpdated, List<PartModel> stockUpdates) {
foreach ( var part in stockUpdates )
{
var partToBeUpdated = stockToBeUpdated.FirstOrDefault(x => x.PartId == part.PartId);
if ( partToBeUpdated != null )
{ partToBeUpdated.Quantity += part.Quantity; }
else
{ stockToBeUpdated.Add(part); }
}
stockToBeUpdated.RemoveAll(x => x.Quantity <= 0);
return stockToBeUpdated;
}
}
Now any class that needs to implement this can simply call it in a method on the respective property. For example, in the EngineerModel class, it can operate on the PartsInStock property:
public void AddPartsToStock(List<PartModel> partsSent) {
PartsInStock.UpdateStockQuantitiesWith(partsSent);
}

Related

Save class properties in a list

I have a class property called Instructions that I use to save my instructions data. I then save my Instruction class with the property to a list called _instructionList. Is this the best way to save my data which I can retrieve later or should I rather use another data structure like Tuple, ArrayList etc?
internal class Instructions
{
public string StreetName { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public string Kind { get; set; }
public double LengthInMeters { get; set; }
}
Put the data coming from ArrayList to the class property and save the class with his properties in the list.
public void SaveInstructions(ArrayList value)
{
_instructionList.Add(new Instructions {
StreetName = (string)value[0],
Latitude = (double)value[1],
Longitude = (double)value[2],
Kind = (string)value[3],
LengthInMeters = (double)value[4]
});
}
ArrayList is mostly used for compatibility with .NET 1.1. There's no reason to use it in new development.
Your question indicates that you're looking for a structure to contain the data for an instance of Instructions so that you can pass it to a method that will 1) create an instance of Instructions and 2) add it to a list.
But the structure you're looking for already exists - it's your Instructions class. Your method might as well look like this:
public void SaveInstructions(Instructions saved)
{
_instructionList.Add(saved);
}
That would be my first choice - create the instance separately and just call the method to add it to the class. That way SaveInstructions has a simpler purpose - to add to the list.
If you did want to pass the parameters (as in your question) then you might do this:
public void SaveInstructions(string streetName, double latitude,
double longitude, string kind, string lengthInMeters)
But that gets a little messy because the method has so many parameters. The first option is better because it doesn't require the method to "know" what all the properties of Instructions are.

Enforcing Equality and Uniqueness Among Inherited Objects

I have a general question about the structure of my object model. Perhaps I am approaching this with tunnel vision from the wrong direction. I have two classes, Item and SerializedItem. I have defined them as such:
public class Item
{
public string ItemNumber { get; set; }
public string Description { get; set; }
public double Cost { get; set; }
}
public class SerializedItem : Item
{
public string SerialNumber { get; set; }
public MyObject Location { get; set; }
}
An Item is a generic definition of an item, and contains information common to that product. SerializedItem is a representation of a specific, physical item. My difficulty lies in the fact that only one Item with a particular ItemNumber should exist in memory at anytime, and I am not sure the best pattern to use to enforce that constraint while allowing a SerializedItem to act as its base type.
Maybe this is a more appropriate approach? I don't have a lot of experience using the 'New' keyword, and I've shied away from using it in the past in favor of an inheritance structure that didn't require its use.
public class Item
{
public string ItemNumber { get; set; }
public string Description { get; set; }
public double Cost { get; set; }
}
public class SerializedItem : Item
{
private Items _item;
public SerializedItemz(Item item)
{
_item = item;
}
public new string ItemNumber
{
get { return _item.ItemNumber; }
set { _item.ItemNumber = value; }
}
public new string Description
{
get { return _item.Description; }
set { _item.Description = value; }
}
public new double Cost
{
get { return _item.Cost; }
set { _item.Cost = value; }
}
public string SerialNumber { get; set; }
}
I would appreciate any guidance on how to approach this. I'm not tied to any particular solution.
To provide some clarity:
The Item class is a representation of a particular product, 'Widget A.' It has information about the Widget A's cost, weight, dimensions, etc. No matter how many Widget As are produced, they all share this information.
The SerializedItem class is a representation of an actual item in that product line, 'Widget A 001.' It contains information about the physical location of that item and it's production and sales history.
If the Item object is updated, all SerializedItems should reflect that change.
I am not sure the best pattern to use to enforce that constraint while allowing a SerializedItem to act as its base type
At first glance a flyweight factory pattern would seem appropriate. Create a class whose responsibility is to create Items, keep track of which ones have already been created, and ensure that only one item with a given key is created.
You can also build logic into the factory to create different subtypes like SerializedItem - you'd just need to provide the appropriate SPI to determine what type is necessary and collect the necessary inputs.
A basic implementation would look something like:
public static class ItemFactory
{
public static Dictionary<string, Item> _Items = new Dictionary<string, Item>;
public static Item GetItem(string itemNumber)
{
if(!_Items.ContainsKey(itemNumber))
{
_Items[itemNumber] = new Item(itemNumber);
// Initialize item if necessary
}
return _Items[itemNumber];
}
}
The SerializedItem class is a representation of an actual item in that product line
Than an appropriate design is to make Item an ItemType and use composition instead of inheritance. So your second approach (with the change that SerializedItem does NOT inherit from Item) looks valid.
If Item is truly a non-instantiated base class then mark it as abstract and work through your concrete SerializedItem class ( and any other derived classes you may have ). If you only want a single Item in memory with a given item number then you might consider a Dictionary type collection indexed on the item number.

Joining two datatables as parent-child in linq

I need to build a typed list of parent-child objects that are read from two different Excel sources: One describes parent object, another describes child objects. The hierarchy is only 2 layers ever.
Reading into excel is not the issue, as it is read into 2 untyped datatables, but joining the information is.
The structure is very plain:
Parent has an ID and some text fields
Children have a parentID (so its 1-m) and some text fields
The objects that these are to be populated into looks like this:
public class ParkingSites
{
public List<ParkingLot> Lots { get; set; }
public ParkingSites(List<ParkingLot> arg)
{
Lots = arg;
}
}
public class ParkingLot
{
public List<Bay> Bays{ get; set; }
public int Id { get; set; }
public List<string> ParkingLotDetails { get; set; }
public ParkingLot()
{
}
}
public class Bay
{
public List<string> BayDetails { get; set; }
public int ParentId { get; set; }
public Bay()
{
}
}
The excel sources have a fixed column order with the parent sheet's first column being the parentId, and the first column on the child sheet also being the parentId.
EDIT: After playing around a bit, I just made both parent and child classes typed, as the initial reason for leaving them mostly untyped lead to more problems than it prevented. This is part of a larger project where the untypedness is a better solution for our problem on the other classes with data that is not hierarchial.
You can simply group the list of children by the parent id, and then iterate over the parents and add each child that belongs to it.
For example, you could use ToLookup:
// assuming you have all Bay instances in a collection called bays
var baymap = bays.ToLookup(b => b.ParentId);
// and all ParkingLot instances in a collection called lots
foreach(var lot in lots)
lot.Bays.AddRange(baymap[lot.Id]);
or, using the first element in the details lists:
var baymap = bays.ToLookup(b => b.BayDetails[0]);
foreach(var lot in lots)
lot.Bays.AddRange(baymap[lot.ParkingLotDetails[0]]);
or, using Where without a lookup (probably slower, depends on your data):
foreach(var lot in lots)
lot.Bays.AddRange(bays.Where(b => b.ParentId == lot.Id));

How to store an arbitrary number of arrays of an arbitrary type in a dictionary

I am trying to build a data structure that can store the trial by trail results of a group of different tests I am running. The test all consist of a number of trails but some of the information that I want to save and later use is different for the different tests. For example, the results of TestA might look like:
Trial(int) WasResponseCorrect(bool) WhichButtonWasPressed(string) SimulusLevel(double)
1 false "Up" 3.5
2 true "Left" 6.5
Where TestB might have different types of result fields:
Trial(int) WasResponseCorrect(bool) ColorPresented(string) LetterPresented(char) LetterGuessed(Char)
1 false green G C
2 false blue H F
I was thinking of creating a dictionary with the field names as the keys (ex. WasResponseCorrect) and an array of the field values as the values of dic. I can't figure out how to do that. Maybe there is a better way to store the information but I can't think of how to do it. I am working with .net (VB and C#) but I think I can understand and convert most any code if you know of examples in other languages. Thanks!
Without knowing more about your requirements (how you are going to store the data, for example), it seems like polymorphism is what you're looking for. That is, you have a superclass (called Trial) and subclasses that represent the specific trial types. For example:
public class Trial {
public int Id { get; set; }
public bool WasResponseCorrect { get; set; } // if this is in every type of trial
// anything else that is common to ALL trial types
}
public class TrialA : Trial {
public string WhichButtonWasPressed { get; set; }
public double SimulusLevel { get; set; }
}
public class TrialB : Trial {
public string ColorPresented { get; set; }
public char LetterPresented { get; set; }
public char LetterGuessed { get; set; }
}
That way you can have a list of Trial objects, but the actual runtime type of those objects can be TrialA or TrialB.

Class Design Question: Union of List<ChildStat> and AllStats

I have a Player class and a Stat class. The Player class has a List property where PlayerStat has a List and XP properties. I think my design is flawed because I am having trouble doing things that I think should be easy. I want to list out all Players and their XP for all Stats. Below are more details of the classes and the GetCompletePlayerStats method which is what I really don't like. I basically need to list out the XP for all stats for a Player, if the player doesn't have a stat then it should have an XP of zero. Any design help/suggestions would be appreciated.
public class Stat : EntityBase{
public virtual string Name { get; set; }
public virtual UnitOfMeasure Unit { get; set; }
public virtual int UnitXP { get; set; }
}
public class Player : EntityBase{
public virtual string Name { get; set; }
public virtual IList<PlayerStat> PlayerStats { get; set; }
public virtual List<PlayerStat> GetCompletePlayerStats(IQueryable<Stat> stats)
{
var allStats = new List<PlayerStat>();
var playerStatIds = PlayerStats.Select(ps => ps.PlayerStatistic.Id).ToList();
if (playerStatIds.Count == 0)
{
allStats.AddRange(stats.Select(stat => new PlayerStat() {PlayerStatistic = stat, XP = 0}));
}
else
{
var zeroStats = stats.Where(s => !playerStatIds.Contains(s.Id)).ToList();
allStats.AddRange(zeroStats.Select(zeroStat => new PlayerStat() {PlayerStatistic = zeroStat, XP = 0}));
allStats.AddRange(PlayerStats);
}
return allStats;
}
}
public class PlayerStat : EntityBase{
public virtual Stat PlayerStatistic { get; set; }
public virtual double XP { get; set; }
}
I have to admit, I dont really see a major drawback in your class design so far. Of course I dont have any insight in the greater picture and how your game is designed, since this is only a little section of it.
However, you said it is the GetCompletePlayerStats that you dont really like. I had to read it several times to understand what you are trying to do here. If I saw that right, you just want to return a PlayerStat object corresponding to each given Stat object. I guess Stat has an Id field (you are using it in your method) to compare two of them for semantic equality.
Given, that I made the right assumptions so far (unfortunately, you didnt provide much info), the method can be simplified to something like:
public virtual IEnumerable<PlayerStat> GetCompletePlayerStats(IEnumerable<Stat> stats)
{
foreach(Stat stat in stats)
yield return PlayerStats.FirstOrDefault(s => stat.Id == s.PlayerStatistic.Id) ??
new PlayerStat() {PlayerStatistic = stat, XP = 0};
yield break;
}
This method here doesnt require a IQueryable but rather a IEnumerable to iterate over via a foreach loop, and yield out the corresponding PlayerStats object if there is one, or create a new one with XP set to 0 otherwise (The null-coalescing operator ?? is very useful in those cases).
Hope that helps!
With the existing design, this method can be simplified thus:
public virtual IList<PlayerStat> GetCompletePlayerStats(IEnumerable<Stat> stats)
{
// build a dictionary of existing stats by ID to facilitate
// the join with requested stats (effectively doing a hash join)
var playerStatsById = PlayerStats.ToDictionary(ps => ps.PlayerStatistic.Id);
// for each requested stat, return either the corresponding player stat
// or a zero stat if one isn't found, maintaining the original order of stats
return stats
.Select(s => playerStatsById.ContainsKey(s.Id) ?
playerStatsById[s.Id] :
new PlayerStat { PlayerStatistic = s, XP = 0 })
.ToList();
}
Note that since this is effectively an outer-join operation (you're joining stats with PlayerStats but you also need the non-matches to yield as zero) you can also do it with LINQ's GroupJoin() operation, although I suspect that would be much less readable.
What does bother me about your design is the ambiguity of names, for example you have both the PlayerStat class and the PlayerStatistic property and they mean subtly different things; consider naming things differently and things might look better; in fact that's probably the best design advice I can give you, for any situation :)

Categories