Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

To abstract, or not to abstract

thanks in advance for reading this. I don’t fully understand how/when to use abstracts so I am trying to think about it each project I work on to see if it will all click some day Smile | :)

Also, the mix of accessibility levels (private, protected, internal) with keywords static, abstract, and override tend to leave me a little confused. How do I define this method/property/class....

It's not all a big mystery to me but some projects have me coding in circles when dealing with these topics.

With that said,

I have an application that reads an XML document and outputs text and image files. I’m also storing all of the information in a database. I have it working nicely.

The XML has a standard implementation with required fields and is used by multiple organizations to submit data to my app. All organizations should use (at least) the required nodes/elements that are outlined in the XML implementation guide.

So, I want to have a default data object type to be able to derive a specific organization’s data type for required elements. (If this object is going to be used, these are the fields that must be implemented).

If the org. just uses the default requirements, I can use the default object. If they use additional (optional) fields, I’ll have to create a new type inheriting the default type.

My first thought was to use and abstract class that had protected properties for my bare minimum requirements:

public abstract partial class AbstractDataObject
{
   protected string DataObjectName;
   protected DateTime? DataObjectDate;
   etc...
}

Then, if the organization just uses the required elements of the node and no optional elements, I can use a “default” object.

internal partial class DefaultDataObject : AbstractDataObject
{
   public new string DataObjectName { get; set; }
   public new DateTime? DataObjectDate { get; set; }
   etc...
}

But, if an organization uses optional fields of the required node, I can use a derived organization data object.

internal sealed partial class OranizationDataObject : AbstractDataObject
{
   public new string DataObjectName { get; set; }
   public new DateTime? DataObjectDate { get; set; }
   etc...

   //Optional fields used by this organization
   public string DataObjectCode { get; set; }
   etc...

}

Do I need the abstract class? It seems to me I can just have a DefaultDataObject (something like):

internal partial class DefaultDataObject
{
   public virtual string DataObjectName { get; set; }
   public virtual DateTime? DataObjectDate { get; set; }
   etc...
}

And then:

internal sealed partial class OranizationDataObject : DefaultDataObject
{
   public override string DataObjectName { get; set; }
   public override DateTime? DataObjectDate { get; set; }
   etc...

   //Optional fields used by this organization
   public string DataObjectCode { get; set; }
   etc...

}

I’m just really trying to understand how to define these objects so I can reuse them per organization. Both ways seem to work, but I am hoping to understand how to define them properly.

Getting the XML into above objects:

public DefaultDataObject ExtractXmlData(XContainer root)
    {
        var myObject = (from t in root.
        Elements("ElementA").Elements("ElementB")
              select new DefaultDataObject()
              {
        DataObjectName = (String)t.Element("ChildElement1"),
        DataObjectDate = 
                      Program.TryParseDateTime((String)
                      t.Elements("ChildElement2")
                      .ElementAtOrDefault(0)
        ),
        etc....

OR

public OranizationDataObject ExtractXmlData(XContainer root)
    {
        var myObject = (from t in root.
        Elements("ElementA").Elements("ElementB")
            select new OranizationDataObject()
              {
    DataObjectName = (String)t.Element("ChildElement1"),
    DataObjectDate = Program.TryParseDateTime(
             (String)t.Elements("ChildElement2")
             .ElementAtOrDefault(0)),
    DataObjectCode = (String)t.Element("ChildElement3"),

etc....

Again, thanks for reading. Don't forget to tip your wait staff....

Joe

like image 979
killerbunnyattack Avatar asked Dec 27 '22 07:12

killerbunnyattack


1 Answers

  1. First of all, your base class doesn't need to be abstract if it's a plain DTO class. If you don't have any functionality that needs to be implemented differently by derived classes, you can simply make it a plain base class which will hold common properties.

  2. Next, there is no point in declaring properties in the base class (abstract in your case), if you are going to hide them (using the new keyword). You first code snippet of DefaultDataObject unnecessarily creates a bunch of new properties with the same name. Remove them completely - they are already defined in the base class.

    [Edit] I didn't notice this initially, and @svick warned me, that your base class actually contained fields instead of properties, which makes me wonder why you needed to add the new keyword at all. I went over your code quickly and saw them as properties. In any case, you should never expose public fields - at least change them to auto-implemented properties by adding the { get; set; } block.

    In other words, this would simply work:

    // this doesn't need to be abstract.
    // just put all the common stuff inside.
    public class BaseDO
    {
        // as svick pointed out, these should also be properties.
        // you should *never* expose public fields in your classes.
    
        public string Name { get; set; }
        public DateTime? Date { get; set; }
    }
    
    // don't use the new keyword to hide stuff.
    // in most cases, you won't need that's behavior
    public class DerivedDO : BaseDO
    {
        // no need to repeat those properties from above,
        // only add **different ones**
        public string Code { get; set; }
    }
    
  3. As a side note, but nevertheless important IMHO, you should simplify naming (and make it more clearer what your code does). There is no need to repeat "DataObject" in every property name, for example. But since your code is probably only a simplified version, it doesn't matter.

  4. Lastly, have you heard of XmlSerializer? You don't need to traverse the XML elements manually. It is enough to call XmlSerializer to both serialize and deserialize your data.

like image 169
Groo Avatar answered Dec 29 '22 22:12

Groo