简体   繁体   中英

Class design question

To put this in concise language...

Aim:
To create a class which can load and save a list of objects to/from a data source.

Current Method:
I have created a class which accepts two delegates as construction parameters:

private class Foo
{
    public delegate List<object> LoadObjectsDelegate();
    public delegate void SaveObjectsDelegate(List<object> data);

    private LoadObjectsDelegate _loadDelegate;
    private SaveObjectsDelegate _saveDelegate;

    public Foo(LoadObjectsDelegate loadDelegate, SaveObjectsDelegate saveDelegate)
    {
        _loadDelegate = loadDelegate;
        _saveDelegate = saveDelegate;
    }

    public List<object> Objects {get; private set;}

    public void Load()
    {
        Objects = _loadDelegate.Invoke();
    }

    public void Save()
    {
        _saveDelegate.Invoke(Objects);
    }
}

i was wondering if there was a cleaner way to do this.

By the looks of it i'd say you're trying to implement the Repository pattern, with some extended functionality. I also see no point in trying to inject the load and save logic, as it's the repository's job to implement them. If the logic of loading and saving objects is the same for all objects in your domain, have it implemented in a base class, and override it in derived classes if needed. This article on SO could give you some ideas on how to deal with this pattern.

Well for one thing I'd make it generic ( Foo<T> ) - your callers can always specify object if they really want, and it means they can use it in a more strongly typed way if necessary.

Currently I'm not sure the class is really adding much value, to be honest. It's really just grouping the three values together - and that's all. If that's what you intended, that's fine... but it feels a bit anaemic to me.

It is not cleaner, just another approach. If the behaviour which you are specifying in the delegates does not vary too much, consider the Template Pattern .

private abstract class AbstractFoo
{
    public List<object> Objects { get; private set; }

    public List<object> Load();

    public abstract void Save(List<object> data);

    // add common functionality
}


private class ConcreteFoo : AbstractFoo
{
    public override List<object> Load()
    {
        // do specific stuff
    }

    public override void Save(List<object> data)
    {
        // do specific stuff
    }
}

The disadvantage is that you need a class for each specific behaviour. Thus: If the behaviour given by the delegates varies a lot or you need to dynamically load different behaviours, I find your delegate approach is suitable. I usually find the Template Pattern easier to understand and easier to unit test.

You can go one step further: Behavior is specified by inheritance in the Template Pattern. Instead of inheritance you can specify the behaviour by reference if you choose a more abstract approach with an interface:

private interface IRepository
{
    List<object> Load();
    void Save(List<object> data);
}


private class FooBar
{
    private IRepository repository;
    public List<object> Objects { get; private set; }

    public FooBar(IRepository repository)
    {
        this.repository = repository;
    }

    public void Load()
    {
        Objects = repository.Load();
    }

    public void Save()
    {
        repository.Save(Objects);
    }
}

If you need some specific behaviour you need to implement a class derived from IRepository . This approach is even more easier to understand and unit test than the Template Pattern. I often end up replacing the Template Pattern with the latter approach.

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