编码原则 之 Explicit Dependencies Principle
Explicit Dependencies Principle
The Explicit Dependencies Principle states:
Methods and classes should explicitly require (typically through method parameters or constructor parameters) any collaborating objects they need in order to function correctly.
If your classes require other classes to perform their operations, these other classes are dependencies.
These dependencies are implicit if they exist only in the code within your class, and not in its public interface.
Explicit dependencies appear most often in an object’s constructor, for class-level dependencies, or in a particular method’s parameter list, for more local dependencies.
Classes with implicit dependencies cost more to maintain than those with explicit dependencies.
- They are more difficult to test because they are more tightly coupled to their collaborators.
- They are more difficult to analyze for side effects, because the entire class’s codebase must be searched for object instantiations or calls to static methods.
- They are more brittle and more tightly coupled to their collaborators, resulting in more rigid and brittle designs.
Classes with explicit dependencies are more honest.
- They state very clearly what they require in order to perform their particular function.
- They tend to follow the Principle of Least Surprise by not affecting parts of the application they didn’t explicitly demonstrate they needed to affect.
- Explicit dependencies can easily be swapped out with other implementations, whether in production or during testing or debugging. This makes them much easier to maintain and far more open to change.
The Explicit Dependencies Principle is closely related to the Dependency Inversion Principle and the Hollywood Principle.
Consider the PersonalizedResponse class in this Gist, which can be constructed without any dependencies:
Implicit Dependencies Example
This class is clearly tightly coupled to the file system and the system clock, as well as a particular customer instance via the global Context class. If we were to refactor this class to make its dependencies explicit, it might look something like this:
namespace ImplicitDependencies
{
class Program
{
static void Main(string[] args)
{
var customer = new Customer()
{
FavoriteColor = "Blue",
Title = "Mr.",
Fullname = "Steve Smith"
};
Context.CurrentCustomer = customer; var response = new PersonalizedResponse(); Console.WriteLine(response.GetResponse());
Console.ReadLine();
}
} public static class Context
{
public static Customer CurrentCustomer { get; set; } public static void Log(string message)
{
using (StreamWriter logFile = new StreamWriter(
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"logfile.txt")))
{
logFile.WriteLine(message);
}
}
} public class Customer
{
public string FavoriteColor { get; set; }
public string Title { get; set; }
public string Fullname { get; set; }
} public class PersonalizedResponse
{
public string GetResponse()
{
Context.Log("Generating personalized response.");//a particular Log instance via the global Context class
string formatString = "Good {0}, {1} {2}! Would you like a {3} widget today?";
string timeOfDay = "afternoon";
if (DateTime.Now.Hour < 12)// the system clock
{
timeOfDay = "morning";
}
if (DateTime.Now.Hour > 17)
{
timeOfDay = "evening";
}
return String.Format(formatString, timeOfDay,
Context.CurrentCustomer.Title, //a particular customer instance via the global Context class
Context.CurrentCustomer.Fullname,
Context.CurrentCustomer.FavoriteColor);
}
}
}
Explicit Dependencies Example
In this case, the logging and time dependencies have been pulled into constructor parameters, while the customer being acted upon has been pulled into a method parameter.
The end result is code that can only be used when the things it needs have been provided for it (and whether you scope dependencies at the class or method level will depend on how they’re used by the class, how many methods reference the item in question, etc. – both options are shown here even though in this case everything could simply have been provided as method parameters).
using System;
using System.IO;
using System.Linq; namespace ExplicitDependencies
{
class Program
{
static void Main(string[] args)
{
var customer = new Customer()
{
FavoriteColor = "Blue",
Title = "Mr.",
Fullname = "Steve Smith"
}; var response = new PersonalizedResponse(new SimpleFileLogger(), new SystemDateTime());//class constructor Console.WriteLine(response.GetResponse(customer));//method parameters
Console.ReadLine();
}
} public interface ILogger
{
void Log(string message);
} public class SimpleFileLogger : ILogger
{
public void Log(string message)
{
using (StreamWriter logFile = new StreamWriter(
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"logfile.txt")))
{
logFile.WriteLine(message);
}
}
} public interface IDateTime
{
DateTime Now { get; }
} public class SystemDateTime : IDateTime
{
public DateTime Now
{
get
{
return DateTime.Now;
}
}
} public class Customer
{
public string FavoriteColor { get; set; }
public string Title { get; set; }
public string Fullname { get; set; }
} public class PersonalizedResponse
{
private readonly ILogger _logger; private readonly IDateTime _dateTime; public PersonalizedResponse(ILogger logger,
IDateTime dateTime)//class constructor
{
this._dateTime = dateTime;
this._logger = logger;
} public string GetResponse(Customer customer)//method parameters
{
_logger.Log("Generating personalized response.");
string formatString = "Good {0}, {1} {2}! Would you like a {3} widget today?";
string timeOfDay = "afternoon";
if (_dateTime.Now.Hour < 12)
{
timeOfDay = "morning";
}
if (_dateTime.Now.Hour > 17)
{
timeOfDay = "evening";
}
return String.Format(formatString, timeOfDay,
customer.Title,
customer.Fullname,
customer.FavoriteColor);
}
}
}
See Also
- Dependency Inversion Principle
- New Is Glue (Ardalis.com)
//a particular customer instance via the global Context class
编码原则 之 Explicit Dependencies Principle的更多相关文章
- 编码原则 之 Stable Dependencies
The Stable Dependencies Principle states that “The dependencies between software packages should be ...
- S.O.L.I.D 是面向对象设计(OOD)和面向对象编程(OOP)中的几个重要编码原则
注:以下图片均来自<如何向妻子解释OOD>译文链接:http://www.cnblogs.com/niyw/archive/2011/01/25/1940603.html < ...
- 开放封闭原则(Open Closed Principle)
在面向对象的设计中有很多流行的思想,比如说 "所有的成员变量都应该设置为私有(Private)","要避免使用全局变量(Global Variables)",& ...
- 最少知识原则(Least Knowledge Principle)
最少知识原则(Least Knowledge Principle),或者称迪米特法则(Law of Demeter),是一种面向对象程序设计的指导原则,它描述了一种保持代码松耦合的策略.其可简单的归纳 ...
- 接口分离原则(Interface Segregation Principle)
接口分离原则(Interface Segregation Principle)用于处理胖接口(fat interface)所带来的问题.如果类的接口定义暴露了过多的行为,则说明这个类的接口定义内聚程度 ...
- 依赖倒置原则(Dependency Inversion Principle)
很多软件工程师都多少在处理 "Bad Design"时有一些痛苦的经历.如果发现这些 "Bad Design" 的始作俑者就是我们自己时,那感觉就更糟糕了.那么 ...
- 里氏替换原则(Liskov Substitution Principle)
开放封闭原则(Open Closed Principle)是构建可维护性和可重用性代码的基础.它强调设计良好的代码可以不通过修改而扩展,新的功能通过添加新的代码来实现,而不需要更改已有的可工作的代码. ...
- 单一职责原则(Single Responsibility Principle)
单一职责原则(SRP:The Single Responsibility Principle) 一个类应该有且只有一个变化的原因. There should never be more than on ...
- 【设计原则和编程技巧】单一职责原则 (Single Responsibility Principle, SRP)
单一职责原则 (Single Responsibility Principle, SRP) 单一职责原则在设计模式中常被定义为“一个类应该只有一个发生变化的原因”,若我们有两个动机去改写一个方法,那这 ...
随机推荐
- AUC计算 - 手把手步进操作
2017-07-10 14:38:24 理论参考: 评估分类器性能的度量,像混淆矩阵.ROC.AUC等 http://www.cnblogs.com/suanec/p/5941630.html ROC ...
- PHP 标准规范,PSR-1,PSR-2,PSR-3,PSR-4,PSR-5,PSR-6,PSR-7及其他标准
官方网站:https://psr.phphub.org/ 这里还有其他很多规范,但是很多都是英文. github:https://github.com/summerblue/psr.phphub.or ...
- Web开发——JavaScript基础(JSON教程)
参考: JSON:JavaScript 对象表示法(JavaScript Object Notation). JSON 是存储和交换文本信息的语法.类似 XML. JSON 比 XML 更小.更快,更 ...
- :after和:before 伪类
1 使用伪类画三角形 .div{ margin-top: 100px; margin-left: 100px; } .div:after{ content: ''; display:inline-bl ...
- 如何使用Windows防火墙禁止软件联网
很多软件需要联网,当我们为了“某些目的”,不想让软件联网的时候,我们有没有办法做到呢?答案是肯定的,那就是使用Windows系统自带的防火墙来屏蔽软件的联网,禁止软件出站请求,这样就可以了,下面介绍具 ...
- jmeter将JDBC Request查询出的数据作为下一个接口的参数
现在有一个需求,从数据库tieba_info表查出rank小于某个值的username和count(*),然后把所有查出来的username和count(*)作为参数值,用于下一个接口. tieba_ ...
- Django中一个项目使用多个数据库
在django项目中, 一个工程中存在多个APP应用很常见. 有时候希望不同的APP连接不同的数据库,这个时候需要建立多个数据库连接. 参考:http://blog.csdn.net/songfree ...
- 原生JavaScript写select下拉选择后跳转页面
<select name="molsel_oprate" onchange="javascript:var obj = event.target; var inde ...
- python 中为什么不需要重载 参数*arg和**args
函数重载主要是为了解决两个问题. (1)可变参数类型. (2) 可变参数个数. 另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两 ...
- Vue疑难杂症
安装Vue脚手架的时候 指令:npm install vue-cli -g Microsoft Windows [版本 6.1.7601] 版权所有 (c) 2009 Microsoft Corpor ...