Static Cling

Sticking Your Code To Things Unnecessarily

Static Cling is a code smell used to describe the undesirable coupling introduced by accessing static (global) functionality, either as variables or methods. This coupling can make it difficult to test or modify the behavior of software systems.

Consider the following example:

public class CheckoutController
{
public void Checkout(Order order)
{
// verify payment // verify inventory LogHelper.LogOrder(order);
}
} public static class LogHelper
{
public static void LogOrder(Order order)
{
using (System.IO.StreamWriter file =
new System.IO.StreamWriter(@"C:\Users\Steve\OrderLog.txt", true))
{
file.WriteLine("{0} checked out.", order.Id);
}
}
} public class Order
{
public int Id { get; set; }
}

In the above code, any attempt to unit test the Checkout method will be made much more difficult by the static LogOrder method, which has a dependency on the file system and a particular file path.

While it’s certainly possible to write an integration test that will still log to the chosen path, or to refactor this code so that the file path comes from configuration or something similar, it would be far better if the dependency on the file system didn’t exist, since it isn’t important to what Checkout() is trying to do.

To refactor away from Static Cling, replace the static method call with an instance method call on an instance type (frequently implementing an interface), and use the strategy design pattern (also known as dependency injection) to inject the dependency into the class that needs the functionality.

In the case where the static functionality is not code you control, you can access it through an Adapter. This approach is shown below:

public class CheckoutController
{
private readonly IOrderLoggerAdapter _orderLoggerAdapter; public CheckoutController(IOrderLoggerAdapter orderLoggerAdapter)
{
_orderLoggerAdapter = orderLoggerAdapter;
} public CheckoutController()
: this(new FileOrderLoggerAdapter())
{ } public void Checkout(Order order)
{
// verify payment // verify inventory _orderLoggerAdapter.LogOrder(order);
}
} public static class LogHelper
{
public static void LogOrder(Order order)
{
using (System.IO.StreamWriter file =
new System.IO.StreamWriter(@"C:\Users\Steve\OrderLog.txt", true))
{
file.WriteLine("{0} checked out.", order.Id);
}
}
} public interface IOrderLoggerAdapter
{
void LogOrder(Order order);
} public class FileOrderLoggerAdapter : IOrderLoggerAdapter
{
public void LogOrder(Order order)
{
LogHelper.LogOrder(order);
}
} public class Order
{
public int Id { get; set; }
}

In the above code, the OrderController no longer has a direct dependency on the static LogHelper.LogOrder() method.

It now follows the Explicit Dependencies Principle, since its constructor declares the collaborating types it requires to function.

This would allow the code to be modified in the future by simply passing in a different implementation of the IOrderLoggerAdapter,

and would also allow unit tests to test the other behavior in the Checkout() method without the need for certain drives, paths, or files to exist on the test machine.

If the application is using a container to resolve class dependencies, configuring the runtime behavior of how OrderController will get the classes it depends on would be done in the container’s configuration.

If a container is not in use, or if existing client code needs to continue to call the default constructor of OrderController, a technique called poor man’s dependency injection can be used.

With this technique, a default constructor is configured to call through to the constructor that accepts dependencies, with instances configured that provide the original behavior.

In this case, the default constructor passes a new instance of the FileOrderLoggerAdapter, which contains the original behavior of calling LogHelper.LogOrder().

Although Static Cling refers specifically to references to static methods (or properties), the same consequences occur when instance variables are instantiated and immediate called within a method.

Be careful of where in your code you make decisions about a method or class’s collaborators, and remember that New is Glue if you choose to instantiate a type that has dependencies on infrastructure concerns (e.g. file system, database, etc).

相关链接:

antipatterns

Principles

Static需谨慎的更多相关文章

  1. 从Go、Swift出发:语言的选择需谨慎

    本文转自 : http://www.csdn.net/article/2014-12-09/2823025 摘要:无论是开源的Go,还是闭源的Swift,新的语言总是利弊一体.不过可以确定的是,新的语 ...

  2. IOS开发中重写init方法使用需谨慎

    IOS开发中重写init方法使用需谨慎 今天在写一个小软件的时候出现一点问题,这个软件的功能是搜索全国学校,首页就是搜索输入框,在框中输入完要查询的学校所在省份,点击buttom后就会跳转到对应的视图 ...

  3. PHP就业前景好不好一看便知,转行选择需谨慎!

    随着互联网行业迎来新一波的热潮,更多的年轻人选择软件行业发展.由于互联网本身快速发展.不断创新的特点,决定了只有以快开发速度和低成本,才能赢得胜利,才能始终保持网站的领先性和吸引更多的网民. 互联网的 ...

  4. 借root之名,行流氓之实,劝告,root需谨慎

    20160425++++++ 今日再回头看这篇文章,貌似有点偏激了一点,不过xda论坛上有个疑似kingroot开发团队的用户说明了kingroot确实对supersu做了限制,说是supersu在替 ...

  5. Xcode8-beat升级需谨慎

    Xcode8-beat版本在打开xib文件的时候,出现了如下的弹窗 在这里要选择Cancel,选择Choose后xib文件的verson会改变,那么Xcode7就没法打开了(坑队友啦), 更没法运行 ...

  6. 检验appium环境是否正常:使用appium自动给手机安装app(注意:如果已存在该app,再执行会将原来的卸载再重装,需谨慎)

    (注意:如果已存在该app,再执行会将原来的卸载再重装.泪的教训,我的微信被卸载重装了o(╥﹏╥)o,自动安装app这个步骤需谨慎操作) hi,前面几篇已经讲了appium环境的搭建.设备的连接, 那 ...

  7. Git存储用户名和密码(明文需谨慎)

    当你配置好git后,在C:\Documents and Settings\Administrator\ 目录下有一个 .gitconfig 的文件,里面会有你先前配好的name 和email,只需在下 ...

  8. New需谨慎

    New is Glue When you’re working in a strongly typed language like C# or Visual Basic, instantiating ...

  9. 设计数据库字段或者java中使用boolean型时需谨慎

    boolean型变量只有两个值 false和true,我们在设计数据库字段时或者定义java变量时会使用boolean,通常情况下开关类的变量使用无可非议,但请一定要考虑到扩展性. 使用前请仔细考虑一 ...

随机推荐

  1. itoa()函数

    itoa()函数 itoa():char *itoa( int value, char *string,int radix); 原型说明: value:欲转换的数据.string:目标字符串的地址.r ...

  2. Android 学习书籍下载

    链接:https://pan.baidu.com/s/1Y6LHLJlYDfbNjoMAVjfjMw               密码:ywbk 链接:https://pan.baidu.com/s/ ...

  3. SVN操作步骤

    1.第一次检出 svn co svn://192.168.1.1:5555/MMM 2.代码更新 svn update 3.代码提交 svn add *.c svn commit -m "d ...

  4. InnoDB中锁的算法(1)

    Ⅰ.InnoDB锁算法的介绍 首先明确一点,锁锁住的是什么?锁锁住的是索引 Record Lock 单个行记录上的锁 Gap Lock 锁定一个范围,但不包含记录本身 Next-key Lock Ga ...

  5. linux加固

    1. 账号和口令 1.1 禁用或删除无用账号 减少系统无用账号,降低安全风险. 操作步骤 使用命令 userdel <用户名> 删除不必要的账号. 使用命令 passwd -l <用 ...

  6. Mac 软件

    http://xclient.info/?t=e0ec92ebc9ce8ef7b7a48460b06893fa040ff066   Mac破解版软件网址

  7. jQuery 学习笔记(3)(内容选择器、attr方法、prop方法,类的操作)

    内容选择器: 1.$("div:empty"): 空的div元素 2.$("div:parent"): 非空div元素 3.$("div:contai ...

  8. 【LeetCode每天一题】Jump Game II(跳跃游戏II)

    Given an array of non-negative integers, you are initially positioned at the first index of the arra ...

  9. Centos使用natapp教程

    官网:https://natapp.cn/ 首先在Natapp站注册账号 点击注册 登录后,点击左边 购买隧道,免费/付费均可 根据需要选择隧道协议,这里以web演示,购买隧道 在 https://n ...

  10. VMWare安装CentOS 6.5图解

    1.本安装示例所使用的虚拟机:vmware workstation 12 2.从VMWare创建虚拟机开始,打开VMWare. 操作系统:CentOS 6.5 64位 IP地址:192.168.1.2 ...