使用
dependency injection
.
//GetSATService returns the fake service during testing
var myService = GetSATService();
var serviceQuestions = myService.GetAllQuestions();
或者,最好在对象的构造函数中设置satservice字段(因此,构造函数要求设置satservice)。如果你这样做,测试会更容易。
编辑:
对不起,我在这里详细说明。上面代码中的内容是一个耦合的依赖项,代码在这里创建它使用的对象。依赖注入或控制反转(IOC)模式,会让您断开依赖。(或者简单地说,不要称之为“新的”——让其他东西来做——一些你可以在消费者之外控制的东西。)
有几种方法可以做到这一点,它们显示在下面的代码中(注释解释):
class Program
{
static void Main(string[] args)
{
//ACTUAL usage
//Setting up the interface injection
IInjectableFactory.StaticInjectable = new ConcreteInjectable(1);
//Injecting via the constructor
EverythingsInjected injected =
new EverythingsInjected(new ConcreteInjectable(100));
//Injecting via the property
injected.PropertyInjected = new ConcreteInjectable(1000);
//using the injected items
injected.PrintInjectables();
Console.WriteLine();
//FOR TESTING (normally done in a unit testing framework)
IInjectableFactory.StaticInjectable = new TestInjectable();
EverythingsInjected testInjected =
new EverythingsInjected(new TestInjectable());
testInjected.PropertyInjected = new TestInjectable();
//this would be an assert of some kind
testInjected.PrintInjectables();
Console.Read();
}
//the inteface you want to represent the decoupled class
public interface IInjectable { void DoSomething(string myStr); }
//the "real" injectable
public class ConcreteInjectable : IInjectable
{
private int _myId;
public ConcreteInjectable(int myId) { _myId = myId; }
public void DoSomething(string myStr)
{
Console.WriteLine("Id:{0} Data:{1}", _myId, myStr);
}
}
//the place to get the IInjectable (not in consuming class)
public static class IInjectableFactory
{
public static IInjectable StaticInjectable { get; set; }
}
//the consuming class - with three types of injection used
public class EverythingsInjected
{
private IInjectable _interfaceInjected;
private IInjectable _constructorInjected;
private IInjectable _propertyInjected;
//property allows the setting of a different injectable
public IInjectable PropertyInjected
{
get { return _propertyInjected; }
set { _propertyInjected = value; }
}
//constructor requires the loosely coupled injectable
public EverythingsInjected(IInjectable constructorInjected)
{
//have to set the default with property injected
_propertyInjected = GetIInjectable();
//retain the constructor injected injectable
_constructorInjected = constructorInjected;
//using basic interface injection
_interfaceInjected = GetIInjectable();
}
//retrieves the loosely coupled injectable
private IInjectable GetIInjectable()
{
return IInjectableFactory.StaticInjectable;
}
//method that consumes the injectables
public void PrintInjectables()
{
_interfaceInjected.DoSomething("Interface Injected");
_constructorInjected.DoSomething("Constructor Injected");
_propertyInjected.DoSomething("PropertyInjected");
}
}
//the "fake" injectable
public class TestInjectable : IInjectable
{
public void DoSomething(string myStr)
{
Console.WriteLine("Id:{0} Data:{1}", -10000, myStr + " For TEST");
}
}
上面是一个完整的控制台程序,您可以运行和播放来查看它的工作原理。我试着保持简单,但你有什么问题可以问我。
第二次编辑:
从评论中,可以清楚地看出这是一个操作需求,而不是一个测试需求,因此实际上它是一个缓存。下面是一些适用于预期目的的代码。同样,下面的代码是一个完整的工作控制台程序。
class Program
{
static void Main(string[] args)
{
ServiceFactory factory = new ServiceFactory(false);
//first call hits the webservice
GetServiceQuestions(factory);
//hists the cache next time
GetServiceQuestions(factory);
//can refresh on demand
factory.ResetCache = true;
GetServiceQuestions(factory);
Console.Read();
}
//where the call to the "service" happens
private static List<Question> GetServiceQuestions(ServiceFactory factory)
{
var myFirstService = factory.GetSATService();
var firstServiceQuestions = myFirstService.GetAllQuestions();
foreach (Question question in firstServiceQuestions)
{
Console.WriteLine(question.Text);
}
return firstServiceQuestions;
}
}
//this stands in place of your xml file
public static class DataStore
{
public static List<Question> Questions;
}
//a simple question
public struct Question
{
private string _text;
public string Text { get { return _text; } }
public Question(string text)
{
_text = text;
}
}
//the contract for the real and fake "service"
public interface ISATService
{
List<Question> GetAllQuestions();
}
//hits the webservice and refreshes the store
public class ServiceWrapper : ISATService
{
public List<Question> GetAllQuestions()
{
Console.WriteLine("From WebService");
//this would be your webservice call
DataStore.Questions = new List<Question>()
{
new Question("How do you do?"),
new Question("How is the weather?")
};
//always return from your local datastore
return DataStore.Questions;
}
}
//accesses the data store for the questions
public class FakeService : ISATService
{
public List<Question> GetAllQuestions()
{
Console.WriteLine("From Fake Service (cache):");
return DataStore.Questions;
}
}
//The object that decides on using the cache or not
public class ServiceFactory
{
public bool ResetCache{ get; set;}
public ServiceFactory(bool resetCache)
{
ResetCache = resetCache;
}
public ISATService GetSATService()
{
if (DataStore.Questions == null || ResetCache)
return new ServiceWrapper();
else
return new FakeService();
}
}
希望这有帮助。祝你好运!