I receive a certain category of products from a server. These products all have an ID. The relevant part is something like this:
public class Product
{
public Product(int id)
{
Id = id;
}
public int Id { get; private set; }
}
In the app itself, I am also using an ID of -1 to indicate a default/null option. There are several options like this, e.g. an ID of -2 for "use the same as parent". At first I checked this in the most primitive way.
if(product.Id == -1)
That is not really clean code, so I changed it to a bool property on the product:
public bool IsDefault
{
get { return Id == -1; }
}
Then I noticed that, as the whole class is immutable, this bool can just be an auto-property which can already be assigned on construction.
bool isDefault = id == -1;
Product product = new Product(id, isDefault);
Right at the moment I tried an approach of using an interface and having an own implementation for the default product, like this:
public interface IProduct
{
int Id{get;}
bool IsDefault{get;}
}
public class Product : IProduct
{
public Product(int id)
{
Id = id;
}
public int Id { get; private set; }
public bool IsDefault { get{ return false; } }
}
public class DefaultProduct : IProduct
{
public int Id { get{ return -1; }
public bool IsDefault { get{ return true; } }
}
Now, the last three examples (check in property / constructor parameter / interface implementations) all seem equally "clean" to me and I don't see any advantage one would have over the others.
Is there any good argument (apart from personal opinion) to prefer one over the others?
You don't need the Default version of a product.
You can create constructor without parameters to create default object.
You're overthinking it, and overcomplicating it with the interface.
I would set IsDefault in the constructor based on the passed Id parameter. And maybe even add a default value to that, so that you can instantiate the class without a parameter, and it should be a default product.
public class Product
{
public Product(int id = -1)
{
Id = id;
IsDefault = id == -1;
}
public int Id { get; private set; }
public bool IsDefault { get; private set; }
}
Although this is always easier said than done, the answer came with reevaluating basic assumptions about what certain things actually are and/or represent.
An ID X points to a product with the ID X. That is the purpose of that ID and using it for a different purpose is abusing it. So
if(product.Id == -1)
And
public bool IsDefault
{
get { return Id == -1; }
}
Are both semantically wrong. isDefault is a different part of the configuration of the class.
It being part of the configuration of the class is also the reason to dismiss the last option. A new class should encapsulate new behaviour. In order to achieve the same behaviour, just for a different configuration, the class should be created with just that, a different configuration.
Now, there is the tempting solution of putting IsDefault = id == -1; in the constructor. However, this would violate SRP. The one reason why the Product class should change is if what a product does changes. If tomorrow the ID for a default product changes to -32, there is no change in what the product does.
However, I have a class ProductParser where Products are created. The one reason why that class should change is if how products are parsed from the server to my app changes - and that is exactly the change we would have then.
So, according to clean code, there is only one correct approach here: Determine isDefault wherever it is created and pass it to the constructor of Product, which keeps it as an immutable value.
Related
I have classes that might or might not change their name (and members) during development. My classes are used (in most cases) like enums, but I couldn't use enums because I needed slightly more functionality. Since classes (obviously) don't have an Integer representing them under the surface I need to create some solution for having similar functionality. In other words, I want for each class to be represented by an Integer (or some other unique identifier).
I've created this attribute:
public class IdAttribute : Attribute
{
private int id = -1;
public IdAttribute(int index)
{
this.id = index;
}
public int Id
{
get
{
return id;
}
}
}
And I'm using it as following:
[Id(0)]
public class Hello: Core { }
[Id(1)]
public class Bye: Core { }
As you can see it's quite error prone, since I don't want any class to have the same Id. And thus, optimally I want an automatic generated id, but I don't want it to change if I ever change anything regarding the class, for example the class name or its members.
What's the best way to achieve this?
(I know that in Java, that once you make a class Serializable, you'll get an automatically generated id (is there something like this in C#?).)
EDIT:
The reason I "couldn't" just use enums is because of (mainly) convenience. I have classes which exposes fields in an editor. And in this editor I can select only the appropriate "enums", in some cases only enums which inherits from "Core" will be displayed and in other cases they might inherit from "Tools" or some other class. I hope that cleared up a bit.
Not sure why you'd need to do this, but you could do the following:
[AttributeUsage(AttributeTargets.Class)]
public class IdAttribute:Attribute
{
public Guid Id { get; }
public IdAttribute(string id)
{
Id = new Guid(id);
}
}
And you'd use it like:
[IdAttribute("7d7952d1-86df-4e2e-b040-fed335aad775")]
public class SomeClass
{
//example, you'd obviously cache this
public Guid Id => GetType().GetCustomAttribute<IdAttribute>().Id;
//...
}
Do note, that Guids are not random. If you need a random id, then this isn't the solution. To generate a Guid read comments to your question.
You can handle that through your base class Core:
public abstract class Core
{
public Core()
{
Type myType = this.GetType();
object[] attrs = myType.GetCustomAttributes(typeof(IdAttribute), false);
IdAttribute attr = attrs?.OfType<IdAttribute>().FirstOrDefault();
int id = -1;
if (attr != null) id = attr.Id;
if (!reservedIdentities.ContainsKey(id))
{
reservedIdentities.Add(id, myType);
}
else
{
if (!reservedIdentities[id].Equals(myType))
throw new ArgumentException("Duplicate identities discovered.", nameof(id));
}
}
static Dictionary<int, Type> reservedIdentities = new Dictionary<int, Type>();
//...
}
I try save complex class inside another class. TypeMask contained inside Entity. TypeMask overloads ToString method, and when time to save Entity comes, a want automaticaly save Mask prop in string form, and when I need load Entity class back automaticaly convert this string to TypeMask(constructor of TypeMask can build TypeMask using string). So what have I overload or from what have I inherit to make it real?
public class Entity
{
[Key]
public int Id { get; set; }
public TypeMask Mask { get; private set; }
}
Here's a way to do this:
public class Entity
{
private TypeMask _typeMask;
[Key]
public int Id { get; set; }
public string TypeMaskString { get; set; }
[NotMapped]
public TypeMask Mask
{
get
{
if (this._typeMask == null && !string.IsNullOrEmpty(TypeMaskString))
{
this._typeMaks = new TypeMask(this.TypeMaskString);
// Or some other way to create a TypeMask from string.
}
return this._typeMask;
}
set
{
this._typeMask = value;
this.TypeMaskString = value.ToString();
}
}
}
There are some things to keep in mind here. When your code makes modifications to a TypeMask object you have to set the object again to update TypeMaskString. Of course, this is error-prone and elaborate, so you want to make sure that TypeMask can be modified through only one method (maybe a method in Entity).
The alternative is to have a property like this (skipping null checks for brevity)
public string TypeMaskString
{
get
{
return this.TypeMask.ToString();
}
set
{
this.TypeMask = new TypeMask(this.TypeMaskString);
}
}
Now the TypeMask object can be modified and TypeMaskString will always return an up-to-date value. But this may hit performance because it potentially converts TypeMask to and from string many times. More often than you may suspect, because EF's change tracker will always read TypeMaskString when it executes DetectChanges, which is a process that runs repeatedly.
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.
I've been looking into rules engines and such, but I really am not sure where to start. This is more for experimentation, but I'd like to implement something like this for work in the future. Basically, I have an application where a user submits a form and populates a POCO object with several properties. I want the administrator of the application to be able to define rules based on the properties of said object and store them in a relational database. When the form is submitted, I would then make a decision based on the user defined rules. For example, the admin can go into the application and define rules like following:
if (typeID == 4 && request.benchMarkScore < 10) {
request.denied = true;
request.denyReasons.Add("Score too low for this product");
}
Here's my POCO Object example:
class Request
{
public int benchMarkScore { get; set; }
public int typeID { get; set; }
public double rate { get; set; }
public bool isEligable { get; set; }
public bool denied { get; set; }
public List<string> denyReasons { get; set; }
public Dictionary<string, double> adjustments;
}
Granted I know this is an overly simplified example, but I come across many situations where I users could benefit from this functionality in my applications. I'm not looking for a complete solution, but instead an idea of where to start.
There are a number of ways you could go about this. One suggestion would be to leverage reflection itself, and allow admins to apply a rule. I'm going to keep this simple, but a rule would consist of:
A bunch of properties, operands, and values
The reason(s) for denial.
So let's define that. I am going to keep this simple and just handle equality, you can define additional ones:
public enum Operand
{
Equals
}
Now, we can define an interface called IRule. I am defining an interface so that in the future, you could potentially put special, more complicated, rules in.
public interface IRule<TPOCO> where TPOCO : class
{
bool IsValid(TPOCO poco);
}
And now we'll define our Rule class (Note: this doesn't handle indexed properties):
public class PropertyCompareRule : IRule<Request>
{
private sealed class PropertyCompare
{
public string PropertyName {get; set; }
public Operand Operand {get; set; }
public object Value {get; set;}
public string Reason {get; set; }
}
private List<PropertyCompare> _comparers = new List<PropertyCompare>();
public bool IsValid(Request poco)
{
bool isValid = true; // let's be optimistic!
PropertyInfo[] properties = poco.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where((property) => property.GetIndexParameters().Length == 0 && property.CanRead).ToArray();
foreach(var property in properties)
{
foreach(var comparer in _comparers)
{
bool localIsValid;
if(comparer.PropertyName == property.Name)
{
object val = property.GetValue(poco, null);
switch(comparer.Operand)
{
case Operand.Equals:
{
localIsValid = object.Equals(val, property.Value);
break;
}
}
if(!localIsValid)
{
poco.denyReasons.Add(comparer.Reason);
isValid = false;
}
}
}
}
return isValid;
}
public void AddComparer(string propertyName, Operand op, object value, string reason)
{
_comparers.Add(new PropertyCompare() { PropertyName = propertyName, Operand = op, Value = value, Reason = reason });
}
}
It wouldn't be difficult for you to be able to persist the property name, operand, and value details in a database or other such storage. Assuming we fleshed out our enum above, we could conceivably do:
PropertyCompareRule rule = new PropertyCompareRule();
rule.AddComparer("typeID", Operand.Equal, 4, "Reason 1");
rule.AddComparer("benchMarkScore", Operand.LessThan, 10, "Reason 2");
bool valid = rule.IsValid(somePocoInstance);
Edit: Some notes
I use a localIsValid rather than bailing out at the first opportunity. You can change this if you want, but the idea is that it allows a single rule to have multiple points of deniability. This may or may not be what you wish - but it's easy enough to refactor the code so that it bails out the moment a single property comparison fails.
This is a nit-pick, but generally C# style-guidlines dictate properties shouldn't be camel-caps... but that's entirely up to you at the end of the day :)
As I understand you, you are looking for some kind of a scripting system for business rules. I found this blog post where some scripting environment are mentioned.
You can also create assemblies on the fly like mentioned here: https://stackoverflow.com/a/4181855/1229622.
Is it possible to have a HasMany relationship of a basic type such as String, on an ActiveRecord class, without the need for creating another entity such as (TodoListItem) to hold the value.
[ActiveRecord]
public class TodoList
{
[PrimaryKey]
public int Id
{
get { return _id; }
set { _id = value; }
}
[HasMany(typeof(string)]
public IList<string> Items
{
get { return _items; }
set { _items= value; }
}
}
Can anyone help?
Yes, you can do this. You can map a one-to-many relation to a built-in or simple type (value type or string) rather than a persisted type.
You'll need to specify the ColumnKey, Table and Element params in the HasMany attribute declaration to get it to wire up properly. You have to have a surrogate key column so the AR can handle updates and cascades, and then Element tells AR which column in the table holds the simple value it will use to make the list.
[HasMany(typeof(string), Table="ToDoList_Items",
ColumnKey = "ListItemID", Element = "Item")]
public IList<string> Items { get; set; }
(or something similar - I haven't got a compiler handy on this box to check it; but per the API docs it ought to work.)
Speaking of which, if you haven't already had a look, http://api.castleproject.org is kinda indispensible for any work with the Castle stack.
In ActiveRecord, your types map to a record in a table (by default). It seems like you are confusing how this type should map to your table.
The MyClass type should have a definition something like this (excluding the PK settings):
[ActiveRecord(Table = "MyTable")]
public class MyClass : ActiveRecordBase<MyClass>
{
[Property]
public int Id { get; set; }
[Property]
public int MyClassId { get; set; }
[Property]
public string ListItem { get; set; }
}
Then, to load the list:
public void LoadMyClasses()
{
MyClass[] results = MyClass.FindAll();
}
I'd suggest you spend some time with the ActiveRecord documentation (or tutorial) as that should also help clear up any confusion.