简体   繁体   中英

How can I use object oriented principles in the following scenario?

I have 3 car rental agencies. Each agency requires the data in XML, but in a different format, for example:

Agency 1

<Rental>
<Customer>
<FirstName>test</FirstName>
<LastName>test</LastName>
</Customer>
<Pickup date="07/20/2012"/>
<Dropoff date="07/25/2012"/>
<Deposit cost="100"/>
</Rental>

Agency 2

<Rental>
<Customer>
<FirstName>test</FirstName>
<LastName>test</LastName>
</Customer>
<Pickup>07/20/2012</Pickup>
<Dropoff>07/25/2012</Dropoff>
<Deposit>100</Deposit>
</Rental>

Agency 3

<Rental pickup="07/20/2012" dropoff="07/25/2012" deposit="100">
<Customer>
<FirstName>test</FirstName>
<LastName>test</LastName>
</Customer>
</Rental>

As you can see from above, all 3 basically contain the same information, altough this doesn't have to be the case (some can contain more or less information), but it is structured different, so the way I access it is different. What is the best approach to take so I can write the most minimum code, but be able to adapt to new rental agencies that come along with a different structure?

Right now, I am doing something like this:

public class Agency1
{
SubmitRental()
{
//Parse XML for Agency 1
}

//Other methods for agency 1
}

public class Agency2
{
SubmitRental()
{
//Parse XML for Agency 2
}

 //Other methods for agency 2
}

public class Agency3
{
SubmitRental()
{
//Parse XML for Agency 3
}

//Other methods for agency 3
}

In the above, the classes contain the same methods, but the way they are implemented is different. There are some methods, properties, etc that are in some classes, but not in others. Are interfaces the best way to approach this? If so, should everything be made an interface?

In the XML samples above, the data was the same, but the format was different, which led some to bring up mapping the all the different formats to a set of common classes, but what about the scenario where not only is the format different, but the data is different as well?

I would typically separate the parsing from the data representation, something like:

public class Rental
{
    public string CustomerFirstName { get; set; }
    public DateTime Pickup { get; set; }
    // ... etc ...
}

And a set of data parsers:

public interface IAgencyRentalParser
{
    XmlDocument ToXml(Rental rental);
    Rental FromXml(XmlDocument xml);
}

public class Agency1RentalParser : IAgencyRentalParser { ... }

public class Agency2RentalParser : IAgencyRentalParser { ... }

public class Agency3RentalParser : IAgencyRentalParser { ... }

And some other mechanism for determining which parser to use. Like maybe a factory pattern that returns the appropriate parser...

public class AgencyRentalParserFactory
{
    public IAgencyRentalParser GetParserFor(XmlDocument xml)
    {
        // create and return one of the parsers...
    }
}

inheritance and polymorphism

public class Agency
{
    public virtual void SubmitRental()
    {
        //can leave empty, or provide default behavior
    }

    // methods and properties/fields common to all Agencies.  
}

public class Agency1 : Agency
{
    public override void SubmitRental()
    {
        //insert submit logic here
    }
    //methods and properties/fields specific to Agency1
}

If they are implemented completely differently you can use Interfaces

http://msdn.microsoft.com/en-us/library/ms173156.aspx

Just create one base abstract class that will hold all information that is common for every agency and all methods marked as virtual. Than create concrete classes for every agency.

public abstract class AgencyBase
{
    private string _customer;
    // other fields that every agency have.

    public abstract void SubmitRental();

//Other methods for agency
}

public class Agency1 : AgencyBase
{
    // Agency specific fields.

    public override void SubmitRental()
    {
        // Your implementation.
    }
}

One possible approach is creating an abstract base class Agency, where you'll hold common data. This class has an abstract method called submitRental(), like:

public abstract class Agency {
  public abstract void SubmitRental();

  //Other methods for agency ...
}

public class AgencyOne : Agency {
  public void SubmitRental() {
    // agency one specific code...
  }
}

Another approach is creating an interface, containing the method SubmitRental(). For each agency, you inherite your interface and write the specific parsing for that agency format.

If I would be the one designing it, here is the simplistic way I would look at it:

1) Agency Object:

ID (string to identify 'Agency 1','Agency 2' and so on.)
Customer (Reference to Person Object)
Shipping (Reference to Shipping Object)

SubmitRental method - should contain customizations based on ID field and other data

2) Person Object:

FirstName
LastName

3) Rental Object:

Pickup Date
Dropoff Date
Deposit

Off course, put all other fields/get/set methods you can put around the above.

By making a BaseAgencyClass, and/or an AbstractAgencyClass, and/or an IAgency interface, you're simply preparing yourself to make MULTIPLE IMPLEMENTATIONS (which is what you want to avoid) of the single Agency functionality. Base Classes and Abstract Classes and Interfaces are [very] appropriate when used properly, but they won't solve what you're looking to do. Instead, solve the problem with your parser.

Make a single class [Agency] (and use an interface and/or base class(es) if appropriate for your solution) and a single universal parser for all Agencies. You want to get the various formats of XML into a common Agency object as soon as possible. Don't persist the differences from XML into your Agency implementations.

For your parser (it could be a method on Agency, or built into Agency's constructor, or another object responsible for parsing, whatever), simply parse through the XML in a dynamic way. All the XML is simply a series of PARENT/CHILD relationships. With XML, there's always a ROOT element, so you can always start with an ELEMENT. Then to find the CHILD that should be next, first examine the ELEMENT's ATTRIBUTES. If you don't find it, then examine the ELEMENT's CHILDREN (nested elements). If you don't find it then, then examine the ELEMENT's VALUE (the text held within the ELEMENT).

You should be able to chew through your XML in this way, and parse all the combinations into a COMMON Agency Object. No code duplication or multiple implementations required. Good luck!

EDIT: Here's an example of writing a "universal parser." This is not production code but just a quick and dirty example using your 3 XML samples:

class Program
{
    static void Main(string[] args)
    {
        IList<XDocument> xmls = new List<XDocument>()
                                    {
                                        XDocument.Load("XMLFile1.xml"),
                                        XDocument.Load("XMLFile2.xml"),
                                        XDocument.Load("XMLFile3.xml")
                                    };
        Agency agency = new Agency();

        foreach (var xml in xmls)
        {
            var rental = new Rental();
            #region Pickup Date
            if (xml.Root.Attribute("pickup") != null)
                rental.Pickup = DateTime.Parse(xml.Root.Attribute("pickup").Value);
            else if (xml.Root.Element("Pickup") != null)
            {
                var pickupElement = xml.Root.Element("Pickup");
                rental.Pickup = DateTime.Parse(pickupElement.Attribute("date") != null ? pickupElement.Attribute("date").Value : pickupElement.Value);
            }
            // else there's no pickup date defined
            #endregion
            agency.rentals.Add(rental);
        }
    }
}

public class Agency
{
    public IList<Rental> rentals = new List<Rental>();
}

public class Rental
{
    public DateTime Pickup;
}

As an example, this only parses the "pickup date" out and populates it into the singular Agency object. It handles the 3 scenarios that "pickup date" may be found in. Like I said, this is not production code so you'd want to write it in a safer and more appropriate way. For example, XML is case sensitive, so it may be a good idea to convert your XML to all UPPER or LOWER (if possible) before parsing. Or you could also use Regex to convert just ELEMENT & ATTRIBUTE names to UPPER but not affect actual values, if that is important.

If what you are saying is that the data may come in a completely unknown XML layout, then this may not work. But this can at least address your known layouts, and valid XML can only be arranged so many ways! Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM