|
公司内部例会讲稿 Tips Click below to go directly to a specific section: UCleus/2产品开发/2实现/Nucleus1.0/Doc/The%20Programming%20Language%20Tips.htm#重载与重写">重载与重写 静态构造函数 只读代理 同步代理 资源管理 内存管理 问题 日常讨论中,术语的不统一带来些许混乱 惯用的表达 | overload | 重载 | 函数名称相同,参数不同(严格的定义还有其它一些限制) | 静态决议 | | override | 重写(覆写,覆盖,改写) | 子类重新定义父类定义过的虚函数(个别语言允许返回值,访问级别可以不同) | 动态决议 | 示例 | class Base { } class Derived:Base { } class Client { void Test(Base obj){ Console.WriteLine("base"); } void Test(Derived obj){ Console.WriteLine("derived"); } static void Main(string[] args) { Base obj = new Derived(); new Client().Test(obj); //输出“base” } } | 静态构造函数问题 1,在工具类中,通常有一些初始化需要在任何静态方法被调用前进行,如配置信息的读取 2,普通类中的复杂的静态信息,需要在任何实例方法被调用前初始化 我见过的解决方法 | 1,在每个静态方法中都调用必需的初始化步骤 public class SomeUtilClass { private SomeUtilClass(){ } private static void Init(){ //.... } public static string GetUID(){ Init(); return uid; } public static string GetConnectionString(){ Init(); return connString; } } | 2,在普通构造函数中初始化 public class SomeMapperClass{ private static Hashtable types; public SomeMapperClass(){ if(types == null){ types = new Hashtable(); types.Add("RED", Color.Red); types.Add("GREEN", Color.Green); types.Add("BLUE", Color.Blue); } } public Color GetColor(string color){ return (Color)types[color]; } } | 我推荐的解决方法 使用静态构造函数(C#),或静态初始化块(Java) | [C#] public class SomeClass { static SomeClass(){ Init(); types = new Hashtable(); types.Add(...); types.Add(...); } } | [Java] public class SomeClass { static{ Init(); types = new HashMap(); types.put("", ""); types.put("", ""); } } | 效果 1,Once,only once 2,定义中对异常处理等有要求,可参考规范 2,多线程时是否有问题,我不清楚,讨论一下 只读代理问题 对象内部有一个集合,由这个对象来控制其元素的增加删除,但客户需要访问该集合取得自己想要的信息,而对象不可能为所有的客户都提供对应的方法,因此需要返回内部的这个集合,但不允许客户增加或删除其元素 我见过的解决方法 | 直接返回代表集合的成员引用,仅在文档中要求客户不能增删集合中的元素 public class SomeClass { private List attrs; public List GetAttributes(){ return attrs; } } | 我推荐的解决方法 1,首选语言提供的功能 2,次选类库提供的功能 3,自己包装代理类,或返回深度拷贝,或使用AOP | [C++] class config { public: const list<string> & get_attributes(){ return attrs; } private: list<string> attrs; }; | [C#] public class SomeClass { private IList attrs; public IList GetAttributes(){ return ArrayList.ReadOnly(attrs); } } | [Java] public class SomeClass { private List attrs; public List getAttributes(){ return Collections.unmodifiableList(attrs); } } | 效果 1,语言提供的功能可帮助在编译期进行检查,确保程序中连试图增删元素的代码都不存在;但对有意无意的const转型无能为力 2,类库提供的功能可帮助在运行期进行检查,确保程序中试图增删元素的操作都抛出异常 同步代理问题 为了对象的线程安全引入了同步机制,却使对象在单线程环境下付出了不必要的性能上的代价,曾经的例子如写时拷贝COW 我见过的解决方法 就是视而不见,不做任何处理,使用同步原语 | [C#] public class SomeClass { [MethodImplAttribute(MethodImplOptions.Synchronized)] public void Add(string name){ attrs.Add(name); } } | [Java] public class SomeClass { public synchronized void Add(string name){ attrs.add(name); } } | 我推荐的解决方法 参考类库的实现,提供没有同步的原始类,及有同步的代理类;早期的JDK中Vector及HashTable都是同步的类,新的ArrayList及HashMap都不是同步的,Collections提供了静态方法返回同步代理;当在多线程环境中需要更改集合时,使用代理类 | [C#,多线程环境中使用同步代理的客户类代码] public class SomeClass { public SomeClass(IList source){ attrs = ArrayList.Synchronized(source); } public void Add(string name){ attrs.Add(name); } public void Remove(string name){ attrs.Remove(name); } } [C#,单线程环境中使用同步代理的客户类代码] public class OtherClass{ public OtherClass(IList source){ attrs = source; } public void Add(string name){ attrs.Add(name); } public void Remove(string name){ attrs.Remove(name); } } | [Java,多线程环境中使用同步代理的客户类代码] public class SomeClass { public SomeClass (List source){ attrs = Collections.synchronizedList(source); } public void add(string name){ attrs.add(name); } } [Java,单线程环境中使用同步代理的客户类代码] public class OtherClass{ public OtherClass(List source){ attrs = source; } public void add(string name){ attrs.add(name); } } | 效果 不必为不需要的功能付出额外的代价 问题 有时需要精确的控制资源分配和释放的时机,保证资源的异常安全,避免资源泄漏,导致死锁,文件丢失,数据库连接过多等 我见过的解决方法 在缺乏真正的局部对象和析构函数的语言中,try/catch/finally充斥在代码中 使用中间件可帮助解决部分资源管理,如数据库连接等 可能会出现基于AOP的资源管理框架 我推荐的解决方法 在C++中,自动化的资源管理是与生俱来的,即B.S.提出的“资源管理即初始化”(RAII) 在C#中,可使用using+IDispose取得近似RAII的效果 在Java中,我不知道,讨论一下 | [C++,RAII,仅仅示例,操作文件应首选std::ftream等] class File { public: eXPlicit File(string path){ pf = fopen(path.c_str(), "rwb"); } ~File(){ fclose(pf); } operator FILE* (){ return pf; } private: FILE* pf; }; [C++,RAII的客户代码,仅仅示例,操作文件应首选std::ftream等] void test() { File file("auto.txt"); char buf[256]; fread(buf, 0, 256, file);//即使这个操作会抛出异常,文件依然会被关闭 } | [C#,仅仅示例] public class File:IDisposable { private FileStream fs; public File(string path){ fs = new FileStream(path, FileMode.Create); } public static implicit operator FileStream(File file) { return file.fs; } public void Dispose() { fs.Close(); } } [C#,仅仅示例] public class Test{ void test(){ using(File file = new File("auto.txt")){ //some read, write, etc. } //文件已经被关闭,即使某步操作抛出异常 } } | 效果 1,资源管理自动化,不局限于内存 2,C++中使用模板,可统一定义大部分资源的包装类,目前的C#只能为每种资源定义单独的类,或者使用AOP 问题 避免Java中的内存泄漏 解决方法 讨论 【推荐参考资料】 1.C#标准:ECMA-334 : C# Language Specification 2.Java标准:The Java? Language Specification Second Edition 3.C++标准:ISO/IEC 14882:2003 Programming Languages - C++ 4.The C# Programming Language 5.The Java Programming Language 6.The C++ Programming Language
|