解耦问题-改进和替代方案

时间:2018-08-09 05:19:22

标签: c# asynchronous json.net solid-principles

我正在学习SOLID原理-特别是控制-DI-去耦的倒置,并且在我查看我的代码之一时,我注意到这种方法(请参阅下文)引起了我的注意。

此代码将由需要读取json文件的任何方法调用,并接受将用于在json文件上查找的字符串值。但是正如您所看到的(为了简化本主题,我简化了代码-排除了异常处理),我不确定从哪里开始(发生了很多初始化或依赖项吗?),我不确定从哪里开始开始)。

这种方法/场景可以作为一个很好的开始吗?您认为我应该保留哪个?需要解耦吗?

谢谢。

public async Task<object> ReadJsonByKey(string jsonPath, string jsonKey)
{
    // First - is it okay to have an initialization at this stage?
    var value = new object();     

    // Second - is this fine to have this in the scope of this method?
    using (TextReader reader = File.OpenText(jsonPath))  
    {
        // Third -  Calling Jobject that accepts new instance of JsonTextReader
        var jObject = await JObject.LoadAsync(new JsonTextReader(reader)); 
        obj = jObject.SelectToken(jsonKey);
    }
    return value;
}

我之所以问这个原因是因为(基于标准)松耦合的东西很容易测试-即单元测试

[UnitTestSuite]
    [TestCase1]
        // Method should only be able to accept ".json" or ".txt" file

    [TestCase2]
        // JsonPath file is valid file system

    [TestCase3]
        // Method should be able to retrieve a node value based from a specific json and key

    [TestCase4]
        // Json-text file is not empty

3 个答案:

答案 0 :(得分:1)

您似乎正在尝试将基础结构问题与应用程序代码脱钩。

假设是这种情况,您需要一个负责读取数据的类:

public interface IDataReader
{
     Task<object> ReadJsonByKey(string jsonPath, string jsonKey)
}

其实现将是您的上述代码:

public class DataReader : IDataReader
{
    public async Task<object> ReadJsonByKey(string jsonPath, string jsonKey)
    {
        // First - is it okay to have an initialization at this stage?
        var value = new object();     

        // Second - is this fine to have this in the scope of this method?
        using (TextReader reader = File.OpenText(jsonPath))  
        {
            // Third -  Calling Jobject that accepts new instance of JsonTextReader
            var jObject = await JObject.LoadAsync(new JsonTextReader(reader)); 
            obj = jObject.SelectToken(jsonKey);
        }
        return value;
    }
}

但是该类现在同时进行文件读取和反序列化,因此您可以进一步将其分为以下几类:

public class DataReader : IDataReader
{
    IDeserializer _deserializer;

    public DataReader(IDeserializer deserializer)
    {
        _deserializer = deserializer;
    }

    public async Task<object> ReadJsonByKey(string jsonPath, string jsonKey)
    {
        var json =  File.ReadAllText(jsonPath);

        return _deserializer.Deserialize(json, jsonKey);
    }
}

这意味着现在可以独立于文件系统依赖性而对IDeserializer进行单元测试。

但是,主要好处应该是,现在可以在对应用程序代码进行单元测试时模拟IDataReader实现。

答案 1 :(得分:1)

使功能类似于:

public async Task<object> ReadJsonByKey(TextReader reader, string jsonKey)

该函数现在可与任何TextReader实现一起使用,因此您可以传递一个TextReader,它可以从文件,内存或任何其他数据源读取。

答案 2 :(得分:0)

唯一妨碍您对其进行单元测试的是File引用,它是静态的。您将无法为该方法提供文件,因为它实际上必须存在。有两种解决方法。

首先,如果可能,您可以传递其他方法,而不是传递方法的路径-例如FileStream

第二个,可以说是更好的选择,您可以将文件系统(我建议使用System.IO.Abstractions,然后使用相关的TestingHelpers软件包)提取到一个私有字段中,并通过ctor注入传递依赖关系。

private readonly IFileSystem fileSystem;

public MyClass(IFileSystem fileSystem)
{
    this.fileSystem = fileSystem;
}

然后使用您的方法

fileSystem.File.OpenText(jsonPath);

通过传递MockFileSystem并在内存中创建一个json文件供该方法读取,应该可以轻松地对该方法进行单元测试。单元测试性实际上是一个很好的指标,表明您的方法是可维护的并且具有明确的目的-如果您可以使用不太复杂的单元测试轻松地对其进行测试,那么可能很好。如果不能的话,那肯定是不好的。