Domanda

I'm developing a framework to access the EVE Online API from a C# Application. Essentially, the API works by the client sending a GET request to the EVE Online servers, the servers then send a response in the form of an XML file. My framework will be parsing the information in those XML files into objects. The information from the request cannot be modified (ie. there is no way to change the server data from the client-side application). Because of this, the objects themselves must be immutable.

Now, if you were to look here, you would see that there are a number of different fields that will always be sent with every request. For example, if you were to request a character's Wallet Transactions, you would request

char/WalletTransactions

Which would return something like this

<?xml version='1.0' encoding='UTF-8'?>
<eveapi version="2">
    <currentTime>2010-12-10 22:10:45</currentTime>
    <result>
        <rowset name="transactions" key="transactionID" columns="transactionDateTime,transactionID,quantity,typeName,typeID,price,clientID,clientName,stationID,stationName,transactionType,transactionFor">
            <row transactionDateTime="2010-11-24 20:33:00" transactionID="1625396969" quantity="2" typeName="Armor Plates" typeID="25605" price="314004.67" clientID="1429013925" clientName="DDV 77" stationID="60008992" stationName="Lisudeh IV - Moon 4 - Theology Council Tribunal" transactionType="sell" transactionFor="personal" />
            <row transactionDateTime="2010-11-17 00:15:00" transactionID="1617616497" quantity="2393" typeName="Phased Plasma S" typeID="184" price="14.90" clientID="979676146" clientName="Kaihokohoko McIver" stationID="60001174" stationName="Lisudeh VI - Moon 2 - Kaalakiota Corporation Factory" transactionType="buy" transactionFor="personal" />
            <row transactionDateTime="2010-11-13 05:58:00" transactionID="1613691673" quantity="1" typeName="Survey Scanner I" typeID="444" price="1113.09" clientID="90001413" clientName="Lucius Ventrell" stationID="60004516" stationName="Hek IV - Krusual tribe Bureau" transactionType="sell" transactionFor="personal" />
            <row transactionDateTime="2010-11-13 05:57:00" transactionID="1613691609" quantity="2" typeName="Ship Scanner I" typeID="443" price="501.00" clientID="1612349330" clientName="Homer911" stationID="60004516" stationName="Hek IV - Krusual tribe Bureau" transactionType="sell" transactionFor="personal" />
            <row transactionDateTime="2010-11-13 05:57:00" transactionID="1613691547" quantity="1" typeName="Rudimentary Ship Scanner I" typeID="6527" price="10.00" clientID="1551104262" clientName="Chloe TaTu" stationID="60004516" stationName="Hek IV - Krusual tribe Bureau" transactionType="sell" transactionFor="personal" />
            <row transactionDateTime="2010-11-13 05:57:00" transactionID="1613691498" quantity="2" typeName="150mm Railgun I" typeID="565" price="5002.10" clientID="419113578" clientName="Rashim Xanadu" stationID="60004516" stationName="Hek IV - Krusual tribe Bureau" transactionType="sell" transactionFor="personal" />
            <row transactionDateTime="2010-11-13 05:57:00" transactionID="1613691447" quantity="1" typeName="Small Hull Repairer I" typeID="524" price="14103.98" clientID="703468457" clientName="Orgazzmic" stationID="60004516" stationName="Hek IV - Krusual tribe Bureau" transactionType="sell" transactionFor="personal" />
            <row transactionDateTime="2010-11-13 05:57:00" transactionID="1613691402" quantity="1" typeName="Dual Light Beam Laser I" typeID="452" price="3009.91" clientID="703468457" clientName="Orgazzmic" stationID="60004516" stationName="Hek IV - Krusual tribe Bureau" transactionType="sell" transactionFor="personal" />
            <row transactionDateTime="2010-11-13 05:57:00" transactionID="1613691357" quantity="1" typeName="Small Nosferatu I" typeID="530" price="8106.77" clientID="703468457" clientName="Orgazzmic" stationID="60004516" stationName="Hek IV - Krusual tribe Bureau" transactionType="sell" transactionFor="personal" />
            <row transactionDateTime="2010-11-13 05:56:00" transactionID="1613690953" quantity="4" typeName="Small Energy Transfer Array I" typeID="529" price="13511.27" clientID="703468457" clientName="Orgazzmic" stationID="60004516" stationName="Hek IV - Krusual tribe Bureau" transactionType="sell" transactionFor="personal" />
            <row transactionDateTime="2010-11-13 05:56:00" transactionID="1613690800" quantity="7" typeName="Tripped Power Circuit" typeID="25598" price="90852.35" clientID="467910905" clientName="Galloway Gallegher" stationID="60004516" stationName="Hek IV - Krusual tribe Bureau" transactionType="sell" transactionFor="personal" />
            <row transactionDateTime="2010-11-13 05:56:00" transactionID="1613690762" quantity="1" typeName="Tangled Power Conduit" typeID="25594" price="275.83" clientID="1271418001" clientName="Garthmanx" stationID="60004516" stationName="Hek IV - Krusual tribe Bureau" transactionType="sell" transactionFor="personal" />
        </rowset>
    </result>
    <cachedUntil>2010-12-10 22:25:45</cachedUntil>
</eveapi>

And the object that I need, needs to have all of those fields from each row (transactionDateTime, transactionID, quantity, typeName, typeID, price, clientID, clientName, stationID, stationName, transactionType, transactionFor), so it looks something like

public class WalletTransaction
{
    public string TransactionDateTime { get; private set; }
    public int TransactionID { get; private set; }
    public int Quantity { get; private set; }
    public string TypeName { get; private set; }
    public int TypeID { get; private set; }
    public decimal Price { get; private set; }
    public int ClientID { get; private set; }
    public string ClientName { get; private set; }
    public int StationID { get; private set; }
    public string StationName { get; private set; }
    public Type TransactionType { get; private set; }
    public Account TransactionFor { get; private set; }
    public int JournalTransactionID { get; private set; }

    public WalletTransaction(string transactionDateTime, int transactionID,
                             int quantity, string typeName, int typeID, decimal price, int clientID,
                             string clientName, int stationID, string stationName, Type transactionType,
                             Account transactionFor, int journalTransactionID)
    {
        TransactionDateTime = transactionDateTime;
        TransactionID = transactionID;
        Quantity = quantity;
        TypeName = typeName;
        TypeID = typeID;
        Price = price;
        ClientID = clientID;
        ClientName = clientName;
        StationID = stationID;
        StationName = stationName;
        TransactionType = transactionType;
        TransactionFor = transactionFor;
        JournalTransactionID = journalTransactionID;
    }

    public enum Type
    {
        Buy,
        Sell
    }

    public enum Account
    {
        Personal,
        Computer
    }
}

As you can see, that is quite a hefty constructor, but the object will not be fully initialized unless it has all of the information that was received from the XML file. There are many other objects that are like this and need to behave in this way (immutable, with many different fields)

So the question becomes: Since the objects need to be immutable, and need to have all of these different fields, do I need to use a constructor; or would an object initializer be better in this case, and just let them call the setter methods if they really want to, because it won't have any effect on the server?

È stato utile?

Soluzione

One of my favorite things to do when creating an object from a data source (SQL, files, XML, etc) is make a constructor that takes the data source itself.

In your case, you can make a constructor or static factory method that takes an XmlNode (or however you're representing a transaction) and takes care of reading values from the source itself.

Example of both methods:

public class WalletTransaction
{
    public string TransactionDateTime { get; private set; }
    public int TransactionID { get; private set; }
    public int Quantity { get; private set; }
    // More omitted for length

    // FACTORY METHOD (RECOMMENDED)
    public static WalletTransaction Create(XmlNode node)
    {
        return new WalletTransaction()
        {
            TransactionDateTime = node.Attributes["transactionDateTime"].Value,
            TransactionID = int.Parse(node.Attributes["transactionID"].Value),
            Quantity = int.Parse(node.Attributes["quantity"].Value)
        };
    }

    // CTOR METHOD
    public WalletTransaction(XmlNode node)
    {
        TransactionDateTime = node.Attributes["transactionDateTime"].Value;
        TransactionID = int.Parse(node.Attributes["transactionID"].Value);
        Quantity = int.Parse(node.Attributes["quantity"].Value);
    }
}

A static factory would be my preference here, for one big reason. If an exception is thrown in a constructor (say, an int.Parse fails) the actual exception is wrapped in a Type Initialization Exception. A static factory method does not suffer from this drawback and makes debugging a little easier.

You could also ditch the field initializer list in the static method and instead do

var wt = new WalletTransaction();
wt.TransactionDateTime = node.Attributes["transactionDateTime"].Value;
//etc
return wt;

The advantage of this is the ability to do more complex processing. Ie, maybe a field is optional, or only present when another field is set to true.

Altri suggerimenti

In order to use an object initialiser, your properties must have public setters, so it's hard to make them immutable. I'd recommend using a constructor if you really need the objects to be immutable.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top