I have a design question about constructors. After watching this video from google about "The Clean Code Talks", he talked about the importance of the initializing classes and more in the constructor, and for better testing options. In my code for example i have a wcf service hosted on the iis
in the service svc contructor we initialize the entire service.
BService()
{
if (!m_DAL.Initilaze())
{
throw new Exception("Due to problems in the initialization B2BService is down");
}
m_sBadTransactionDir = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath + "BadTransaction";
_address1 = new EndpointAddress(System.Configuration.ConfigurationManager.AppSettings["1Address"]);
_address2 = new EndpointAddress(System.Configuration.ConfigurationMana开发者_JAVA百科ger.AppSettings["2Address"]);
_address3 = new EndpointAddress(System.Configuration.ConfigurationManager.AppSettings["3Address"]);
_address4 = new EndpointAddress(System.Configuration.ConfigurationManager.AppSettings["4Address"]);
LoadSitesFromAdaptors();
double interval1= Convert.ToDouble(System.Configuration.ConfigurationManager.AppSettings["interval1"]);
m_sentTransactionTimer.Interval = interval1;
m_sentTransactionTimer.Elapsed += Foo1;
m_sentTransactionTimer.Start();
double interval2 = Convert.ToDouble(System.Configuration.ConfigurationManager.AppSettings["interval2"]);
m_checkStatusTimer.Interval = interval2 ;
m_checkStatusTimer.Elapsed += Foo2;
m_checkStatusTimer.Start();
double interval3= Convert.ToDouble(System.Configuration.ConfigurationManager.AppSettings["interval3"]);
m_adaptorsTimer.Interval = interval3;
m_adaptorsTimer.Elapsed += Foo3;
m_adaptorsTimer.Start();
.... and some more initialization code here
Logger.Instance.Write("*************** Service is Up ****************", "INFO");
}
Do you guys know of better ways to initialize big classes ? make it easier to test and generally your opinion on the matter ?
If this is the "main" class of the service, you may not want to unit test it. Or if it has functionality you want unit tested, you may want to move that functionality to separate class(es), which get the required config parameters (and just those) as constructor arguments. Thus they are easily unit testable.
Alternatively, if you really really want to test this class, you could hide System.Configuration.ConfigurationManager
behind a mockable interface, so that you can easily pass in whatever parameters you need for your unit tests. But in general, you will almost always have some high level class(es) in your apps which just initialize the whole stuff, load config parameters from command line / config files / registry / DB / whatever, and pass these around to the other classes which do the real work. In a well designed app, such classes have this single responsibility, and it is validated by integration/system tests rather than unit tests.
On a smaller scale, the code above contains duplication in these segments:
double interval1= Convert.ToDouble(System.Configuration.ConfigurationManager.AppSettings["interval1"]);
m_sentTransactionTimer.Interval = interval1;
m_sentTransactionTimer.Elapsed += Foo1;
m_sentTransactionTimer.Start();
These could be easily extracted into a single method with parameters.
Just having a cursory glance at your code, it seems to me that it should be re factored so that you're avoiding all of your duplicated code.
The best way that I have found to reduce duplication is to either A abstract away duplicated code into small functions with parameters or use dictionaries and list comprehension to create you initial setup and then parse your lists running your methods.
This creates very neat and tidy, easily readable and maintainable code.
Could be refactored into something like
var addressList = new List<string>() { "1Address", "2Address", "3Address" };
var composedAddress = new List<Address>();
addressList.ForEach(address => composedAddress.Add(new EndpointAddress(
System.Configuration.ConfigurationManager.AppSettings[address]))
);
Additionally you can do the same with your intervals;
var intervalList = new List<Tuple<string, timer, delegate>>() {
new Tuple<string, timer, delegate> { "Interval1", m_sentTransactionTimer, Foo1 },
new Tuple<string, timer, delegate> { "Interval2" m_checkStatusTimer, Foo2 },
new Tuple<string, timer, delegate> { "Interval3", m_adaptorsTimer , Foo3 }
}
intervalList.ForEach(tuple => {
var interval = Convert.ToDouble(System.Configuration.ConfigurationManager.AppSettings[tuple.Item1])
tuple.Item2.Interval = interval;
tuple.Item2.Elapsed += tuple.Item3;
tuple.Item2.Start();
});
Also you might want to ensure that your BServiceClass is not taking on more responsibilities than it should. A class should only have 1 responsibility. Following the Single Responsiblity Principle you should consider abstracting out any code that seems to be violating that single responsiblity.
BService Constructor should be nothing but higher order functions which describe what is happening.
BService() {
Initialize(addressList, intervalList, someotherList);
Logger.Instance.Write("*************** Service is Up ****************", "INFO");
}
Initialize(List<string>, List<Tuple<string, timer, delegate>>, ...){
...
}
精彩评论