Effective C# 原则33:限制类型的访问
Item 33: Limit Visibility of Your Types

并不是所有的人都须要知道所有的事。也不是所有的类型须要是公共的。对于每个类型,在满足功能的情况下,应该尽可能的限制访问级别。而且这些访问级别往往比你想像的要少得多。在一个私有类型上,所有的用户都可以通过一个公共的接口来访问这个接口所定义的功能。

让我们回到最根本的情况上来:强大的工具和懒惰的开发人员。VS.net对于他们来说是一个伟大的高产工具。我用VS.net或者C# Builder轻松的开发我所有的项目,因为它让我更快的完成任务。其中一个加强的高产工具就是让你只用点两下按钮,一个类就创建了,当然如果这正是我想要的话。VS.net为我们创建的类就是这样的:
public class Class2
{
  public Class2()
  {
    //
    // TODO: Add constructor logic here
    //
  }
}
这是一个公共类,它在每个使用我的程序集的代码块上都是可见的。这样的可见级别太高了,很多独立存在的类都应该是内部(internal)的。你可以通过在已经存在的类里嵌套一个受保护的或者私有的类来限制访问。 越低的访问级别,对于今后的更新整个系统的可能性就越少。越少的地方可以访问到类型,在更新时就越少的地方要修改。

只暴露须要暴露的内容,应该通过尝试在类上实现公共接口来减少可见内容。你应该可以在.Net框架库里发现使用Enumerator模式的例子,System.ArrayList包含一个私有类,ArrayListEnumerator, 而就是它只实现了IEnumerator接口:

// Example, not complete source
public class ArrayList: IEnumerable
{
  private class ArraylistEnumerator : IEnumerator
  {
    // Contains specific implementation of
    // MoveNext( ), Reset( ), and Current.
  }

public IEnumerator GetEnumerator()
  {
    return new ArrayListEnumerator( this );
  }

// other ArrayList members.
}

对于我们这样的使用者来说,不须要知道ArrayListEnumerator类,所有你须要知道的,就是当我们在ArrayList对象上调用GetEnumerator函数时,你所得到的是一个实现了IEnumerator接口的对象。而具体的实现则是一个明确的类。.Net框架的设计者在另一个集合类中使用了同样的模式:哈希表(Hashtable)包含一个私有的HashtableEnumerator, 队列(Queue)包含一个QueueEnumerator, 等等。私有的枚举类有更多的优势。首先,ArrayList类可以完全取代实现IEnumerator的类型,而且你已经成为一个贤明的程序员了,不破坏任何内容。其实,枚举器类不须要是CLS兼容的,因为它并不是公共的(参见原则30)。而它的公共接口是兼容的。你可以使用枚举器而不用知道实现的类的任何细节问题。

创建内部的类是经常使用的用于限制类型可见范围的概括方法。默认情况下,很多程序员都总是创建公共的类,从来不考虑其它方法。这是VS.net的事。我们应该取代这种不加思考的默认,我们应该仔细考虑你的类型会在哪些地方使用。它是所有用户可见的?或者它主要只是在一个程序集内部使用?
 通过使用接口来暴露功能,可以让你更简单的创建内部类,而不用限制它们在程序集外的使用(参见原则19)。类型应该是公共的呢?或者有更好的接口聚合来描述它的功能?内部类可以让你用不同的版本来替换一个类,只要在它们实现了同样的接口时。做为一个例子,考虑这个电话号码验证的问题:

public class PhoneValidator
{
  public bool ValidateNumber( PhoneNumber ph )
  {
    // perform validation.
    // Check for valid area code, exchange.
    return true;
  }
}

几个月过后,这个类还是可以很好的工作。当你得到一个国际电话号码的请求时,前面的这个PhoneValidator就失败了。它只是针对US的电话号码的。你仍然要对US电话号码进行验证,而现在,在安装过程中还要对国际电话号码进行验证。与其粘贴额外的功能代码到一个类中,还不如了断减少两个不同内容耦合的做法,直接创建一个接口来验证电话号码:

public interface IPhoneValidator
{
  bool ValidateNumber( PhoneNumber ph );
}

下一步,修改已经存在的电话验证,通过接口来实现,而且把它做为一个内部类:

internal class USPhoneValidator : IPhoneValidator
{
  public bool ValidateNumber( PhoneNumber ph )
  {
    // perform validation.
    // Check for valid area code, exchange.
    return true;
  }
}

最后,你可以为国际电话号码的验证创建一个类:

internal class InternationalPhoneValidator : IPhoneValidator
{
  public bool ValidateNumber( PhoneNumber ph )
  {
    // perform validation.
    // Check international code.
    // Check specific phone number rules.
    return true;
  }
}

为了完成这个实现,你须要创建一个恰当的类,这个类基于电话号码类型类,你可以使用类厂模式实现这个想法。在程序集外,只有接口是可见的。而实际的类,就是这个为世界不同地区使用的特殊类,只有在程序集内是可见的。你可以为不同的区域的验证创建不同的验证类,而不用再系统里的其它程序集而烦扰了。

你还可以为PhoneValidator创建一个公共的抽象类,它包含通用验证的实现算法。用户应该可以通过程序集的基类访问公共的功能。在这个例子中,我更喜欢用公共接口,因为即使是同样的功能,这个相对少一些。其他人可能更喜欢抽象类。不管用哪个方法实现,在程序集中尽可能少的公开类。

这些暴露在外的公共类和接口就是你的合约:你必须保留它们。越多混乱的接口暴露在外,将来你就越是多的直接受到限制。越少的公共类型暴露在外,将来就越是有更多的选择来扩展或者修改任何的实现。
 
======================================

Item 33: Limit Visibility of Your Types
Not everybody needs to see everything. Not every type you create needs to be public. You should give each type the least visibility necessary to accomplish your purpose. That's often less visibility than you think. Internal or private classes can implement public interfaces. All clients can access the functionality defined in the public interfaces declared in a private type.

Let's get right to the root cause: powerful tools and lazy developers. VS .NET is a great productivity tool. I use it or C# Builder for all my development simply because I get more done faster. One of the productivity enhancements lets you create a new class with two button clicks. If only it created exactly what I wanted. The class that VS.NET creates looks like this:

public class Class2
{
  public Class2()
  {
    //
    // TODO: Add constructor logic here
    //
  }
}

It's a public class. It's visible to every piece of code that uses the assembly I'm creating. That's usually too much visibility. Many standalone classes that you create should be internal. You can further limit visibility by creating protected or private classes nested inside your original class. The less visibility there is, the less the entire system changes when you make updates later. The fewer places that can access a piece of code, the fewer places you must change when you modify it.

Expose only what needs to be exposed. Try implementing public interfaces with less visible classes. You'll find examples using the Enumerator pattern throughout the .NET Framework library. System.ArrayList contains a private class, ArrayListEnumerator, that implements the IEnumerator interface:

// Example, not complete source
public class ArrayList: IEnumerable
{
  private class ArraylistEnumerator : IEnumerator
  {
    // Contains specific implementation of
    // MoveNext( ), Reset( ), and Current.
  }

public IEnumerator GetEnumerator()
  {
    return new ArrayListEnumerator( this );
  }

// other ArrayList members.
}

Client code, written by you, never needs to know about the class ArrayListEnumerator. All you need to know is that you get an object that implements the IEnumerator interface when you call the GetEnumerator function on an ArrayList object. The specific type is an implementation detail. The .NET Framework designers followed this same pattern with the other collection classes: Hashtable contains a private HashtableEnumerator, Queue contains a QueueEnumerator, and so on. The enumerator class being private gives many advantages. First, the ArrayList class can completely replace the type implementing IEnumerator, and you'd be none the wiser. Nothing breaks. Also, the enumerator class need not be CLS compliant. It's not public (see Item 30.) Its public interface is compliant. You can use the enumerator without detailed knowledge about the class that implements it.

Creating internal classes is an often overlooked method of limiting the scope of types. By default, most programmers create public classes all the time, without any thought to the alternatives. It's that VS .NET wizard thing. Instead of unthinkingly accepting the default, you should give careful thought to where your new type will be used. Is it useful to all clients, or is it primarily used internally in this one assembly?

Exposing your functionality using interfaces enables you to more easily create internal classes without limiting their usefulness outside of the assembly (see Item 19). Does the type need to be public, or is an aggregation of interfaces a better way to describe its functionality? Internal classes allow you to replace the class with a different version, as long as it implements the same interfaces. As an example, consider a class that validates phone numbers:

public class PhoneValidator
{
  public bool ValidateNumber( PhoneNumber ph )
  {
    // perform validation.
    // Check for valid area code, exchange.
    return true;
  }
}

Months pass, and this class works fine. Then you get a request to handle international phone numbers. The previous PhoneValidator fails. It was codedto handle only U.S. phone numbers. You still need the U.S. Phone Validator, but now you need to use an international version in one installation. Rather than stick the extra functionality in this one class, you're better off reducing the coupling between the different items. You create an interface to validate any phone number:

public interface IPhoneValidator
{
  bool ValidateNumber( PhoneNumber ph );
}

Next, change the existing phone validator to implement that interface, and make it an internal class:

internal class USPhoneValidator : IPhoneValidator
{
  public bool ValidateNumber( PhoneNumber ph )
  {
    // perform validation.
    // Check for valid area code, exchange.
    return true;
  }
}

Finally, you can create a class for international phone validators:

internal class InternationalPhoneValidator : IPhoneValidator
{
  public bool ValidateNumber( PhoneNumber ph )
  {
    // perform validation.
    // Check international code.
    // Check specific phone number rules.
    return true;
  }
}

To finish this implementation, you need to create the proper class based on the type of the phone number. You can use the factory pattern for this purpose. Outside the assembly, only the interface is visible. The classes, which are specific for different regions in the world, are visible only inside the assembly. You can add different validation classes for different regions without disturbing any other assemblies in the system. By limiting the scope of the classes, you have limited the code you need to change to update and extend the entire system.

You could also create a public abstract base class for PhoneValidator, which could contain common implementation algorithms. The consumers could access the public functionality through the accessible base class. In this example, I prefer the implementation using public interfaces because there is little, if any, shared functionality. Other uses would be better served with public abstract base classes. Either way you implement it, fewer classes are publicly accessible.

Those classes and interfaces that you expose publicly to the outside world are your contract: You must live up to them. The more cluttered that interface is, the more constrained your future direction is. The fewer public types you expose, the more options you have to extend and modify any implementation in the future.

转载于:https://www.cnblogs.com/WuCountry/archive/2007/03/24/686705.html

Effective C# 原则33:限制类型的访问(译)相关推荐

  1. Effective C# 原则16:垃圾最小化(译)

    Effective C# 原则16:垃圾最小化 Item 16: Minimize Garbage 垃圾回收器对内存管理表现的非常出色,并且它以非常高效的方法移除不再使用的对象.但不管你怎样看它,申请 ...

  2. Effective C# 原则50:了解ECMA标准(译)

    Effective C# 原则50:了解ECMA标准 Item 50: Learn About the ECMA Standard ECMA标准是C#语言所有功能的官方说明.ECMA-334定义了C# ...

  3. Effective C# 原则8:确保0对于值类型数据是有效的(翻译)

    Effective C# 原则8:确保0对于值类型数据是有效的 Ensure That 0 Is a Valid State for Value Types .Net系统默认所有的对象初始化时都为0. ...

  4. Effective C# 原则13:用静态构造函数初始化类的静态成员(译)

    Effective C# 原则13:用静态构造函数初始化类的静态成员 Item 13: Initialize Static Class Members with Static Constructors ...

  5. Effective C# 原则18:实现标准的处理(Dispose)模式(译)

    Effective C# 原则18:实现标准的处理(Dispose)模式 我们已经讨论过,处理一个占用了非托管资源对象是很重要的.现在是时候来讨论如何写代码来管理这些类占用的非内存资源了.一个标准的模 ...

  6. Effective C# 原则11:选择foreach循环

    Effective C# 原则11:选择foreach循环 Item 11: Prefer foreach Loops C#的foreach语句是从do,while,或者for循环语句变化而来的,它相 ...

  7. Effective C# 原则38:使用和支持数据绑定(译)

    Effective C# 原则38:使用和支持数据绑定 Item 38: Utilize and Support Data Binding 有经验的Windows程序员一定对写代码从一个控件上取值,以 ...

  8. Effective C# 原则22:用事件定义对外接口(译)

    Effective C# 原则22:用事件定义对外接口 Item 22: Define Outgoing Interfaces with Events 可以用事件给你的类型定义一些外部接口.事件是基于 ...

  9. Effective C# 原则1:尽可能的使用属性(property),而不是数据成员(field)。

    Effective C# 原则1:尽可能的使用属性(property),而不是数据成员(field).<?xml:namespace prefix = o ns = "urn:sche ...

最新文章

  1. 自学成才翁_如何发挥自学成才的内在游戏
  2. apue学习笔记(第三章 文件I/O)
  3. 织梦调用css的标签,织梦dede常用的调用标签(个人总结)
  4. ubuntu pycharm社区版安装
  5. 在Dialog中实现下拉框效果并对下拉框赋自定义的值
  6. HDFS EditsLog和FsImage日志机制
  7. 2015.5.21 Core Java Volume 1
  8. LeetCode MySQL 1084. 销售分析III
  9. 惠普110a硒鼓加粉步骤_惠普打印机加粉教程(88a硒鼓/36a硒鼓/78a硒鼓)
  10. Android基础 --- Widget
  11. c语言第八周上机作业答案,C语言第五次上机作业参考答案
  12. Memcached Java Client API详解
  13. [BZOJ4066]简单题
  14. 不刷新改变/更换URL: pushState + Ajax
  15. LX04 小米触屏音箱刷机教程
  16. thymeleaf 默认选中下拉框(select option)
  17. Qt Creator配置FreeImage库
  18. 通俗理解逻辑删除和物理删除的区别
  19. 回顾过去。。展望未来
  20. 服务器c盘里会有大量.log文件,电脑C盘爆满怎么办?这3个文件夹放心删,瞬间多出10个G...

热门文章

  1. 无法加载_Microsoft store应用商店无法加载 问题解决
  2. 矩形窗函数的频谱图_读书笔记6-单频矩形脉冲信号
  3. VMLite 安装linux系统,VMlite安装XPMode及xp、win7、linux的虚拟磁盘到D盘的方法(15页)-原创力文档...
  4. vscode extension
  5. SQLAlchemy Tutorial
  6. php中如何滚动文字,HTML_网页HTML代码:滚动文字的制作,制作滚动文字 通过本章前面 - phpStudy...
  7. Exchange 日志/存储路径
  8. Java多线程学习总结(5)——乐观锁和悲观锁的基本概念、实现方式(含实例)、适用场景及常见面试题
  9. Docker学习总结(18)——阿里超大规模Docker化之路
  10. php遍历原理,PHP数组遍历与实现原理