我正在学习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
答案 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文件供该方法读取,应该可以轻松地对该方法进行单元测试。单元测试性实际上是一个很好的指标,表明您的方法是可维护的并且具有明确的目的-如果您可以使用不太复杂的单元测试轻松地对其进行测试,那么可能很好。如果不能的话,那肯定是不好的。