Effective C#之Item 33:Limit Visibility of Your Types

  rel="File-List" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> rel="themeData" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml">

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.

不是所有人需要看到所有东西。不是你创建的所有类型都需要是公开的。应该赋予你的类型完成目标所必需的最小可见性,它们的可见性一般比你想的小。Internal或者private类可以实现公共接口。所有的客户都可以访问在公共接口里定义在私有类型里声明的方法。

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:

让我们看看根本原因:强有力的工具和懒惰的开发者。VS.NET是一个很好的有生产力的工具。我在所有的开发中使用它或者C# Builder,因为我可以做的更多更快。一个生产力方面的增强就是:通过点击两次按钮就能创建一个新类。只要它精确的创建了我想要的东西就好。VS.NET创建的类看起来是这样的:

  1. public class Class2
  2. {
  3.   public Class2()
  4.   {
  5.     //
  6.     // TODO: Add constructor logic here
  7.     //
  8.   }
  9. }

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.

这是一个公共类,对于使用我正创建的程序集的所有代码段来说,都是可见的。一般来说,可见性太大了。很多你创建的孤立类应该是internal的。你可以进一步,通过创建protected或者在原来的类的内部嵌套的private类来限制可见性。可见性越小,以后更新时,整个系统的变化就越少。能访问一段代码的地方越少,以后进行修改时,要修改的地方就越少。

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:

仅仅暴露需要暴露的。尽量用更小可见性的类来实现公共接口。你可以发现使用Enumerator模式的例子遍布了整个.Net框架库。System.ArrayList包含一个实现了IEnumerator接口的私有类:ArrayListEnumerator

  1. // Example, not complete source
  2. public class ArrayList: IEnumerable
  3. {
  4.   private class ArraylistEnumerator : IEnumerator
  5.   {
  6.     // Contains specific implementation of
  7.     // MoveNext( ), Reset( ), and Current.
  8.   }
  9.   public IEnumerator GetEnumerator()
  10.   {
  11.     return new ArrayListEnumerator( this );
  12.   }
  13. // other ArrayList members.
  14. }

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.

你写的客户代码,从不需要知道ArrayListEnumerator类的存在。所有你需要知道的就是,当你在一个ArrayList对象上调用GetEnumerator方法时,将得到一个实现了IEnumerator接口的对象。特定的类型是实现的细节,你不需要知道。.Net框架的设计者遵循了与其它集合类相同的模式:Hashtable包含一个私有的HashtableEnumeratorQueue包含一个私有的QueueEnumerator,等等。私有的枚举类有很多优点。首先,ArrayList类可以完全替代实现了IEnumerator的类型,并且你不会觉得别扭。没有什么会被破坏。同时,枚举类不需要CLS兼容,因为它不是公共的(Item30)。它的公共接口是兼容的。你可以使用这个枚举,同时不需要知道实现它的类的细节。

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?

在限制类型的范围上,创建内部类是一种经常被忽略的方法。默认情况下,多数程序员总是创建公共方法,不会想到去进行修改。那是VS.NET向导的事情。不应该不加思考的接受默认状态,你应该仔细考虑新类型将会用在什么地方,它对于所有的客户都是有用的码?或者,它主要用在这个程序集的内部吗?

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:

通过接口暴露你的功能,使你能够更容易的创建内部类,而不需要限制它们在程序集外部的使用(Item 19)。该类型需要是公共的吗?或者一个接口的集合是更好的方法来描述它的功能吗?内部类允许你使用不同的版本来替换该类,只要它实现了同样的接口。作为例子,考虑一个验证电话号码的类。

  1. public class PhoneValidator
  2. {
  3.   public bool ValidateNumber( PhoneNumber ph )
  4.   {
  5.     // perform validation.
  6.     // Check for valid area code, exchange.
  7.     return true;
  8.   }
  9. }

Months pass, and this class works fine. Then you get a request to handle international phone numbers. The previous PhoneValidator fails. It was coded to 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:

这个类一直工作的很好。数个月之后,你需要根据需求来处理国际电话号码。前面的PhoneValidator就不能适用了,它本来仅仅是用来处理美国电话号码的。现在,你仍然需要美国电话号码验证,同时你需要在一个安装程序里面有国际化的版本。不要将额外的功能挤在这一个类里面,更好的方法是减少不同项目之间的耦合度。通过创建一个接口来验证任何电话号码:

  1. public interface IPhoneValidator
  2. {
  3.   bool ValidateNumber( PhoneNumber ph );
  4. }
  5.  

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

接下来,修改已经存在的电话号码验证对象来实现该接口,使它成为一个内部类:

  1. internal class USPhoneValidator : IPhoneValidator
  2. {
  3.   public bool ValidateNumber( PhoneNumber ph )
  4.   {
  5.     // perform validation.
  6.     // Check for valid area code, exchange.
  7.     return true;
  8.   }
  9. }

 

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

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

  1. internal class InternationalPhoneValidator : IPhoneValidator
  2. {
  3.   public bool ValidateNumber( PhoneNumber ph )
  4.   {
  5.     // perform validation.
  6.     // Check international code.
  7.     // Check specific phone number rules.
  8.     return true;
  9.   }
  10. }

 

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.

你也可以为PhoneValidator创建公共抽象基类,用来包含通用的实现算法。客户可以通过可访问的基类来访问公共方法。在该例子中,我更喜欢使用公共接口的实现方法,因为这使得共享的方法更少(如果有的话)。对于抽象基类,可能会更好的服务于其它用处。不管你用哪种方法实现,目的都是让可以公开访问的类更少。

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.

这些你公开暴露给外部世界的类和接口,是你的约定:你必须依靠它们。接口越混乱,以后的扩展方向就越受限制。暴露的公共类型越少,以后在扩展或者修改任何实现时,你的选择就越多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值