封装特性为程序设计提供了系统与系统、模块与模块、类与类之间交互的实现手段。隐藏了内部的具体实现细节,对外提供了统一的访问接口,来操作内部数据成员。}
using System; namespace ConsoleApp1 { public class ATM { //定义私有方法,隐藏具体实现 private Client GetUser(string userID) { } private bool IsValidUser(Client user) { } private int GetHash(int money) { } //定义公有方法,提供对外接口 public void CashProcess(string UserId, int money) { Client tempUser = GetUser(userID); if (IsValidUser(tempUser)) { GetCash(money); } else { Console.WriteLine("你不是合法用户"); } } } public class Client { } class Program { static void Main(string[] args) { } } }
字段:通常定义为private,标识类的状态信息。CLR只支持只读和读写字段。只读地段只能在构造函数中被赋值。
public class Client { private string name; private int age; private string password; }
我们可以通过类的实例访问和改变字段内容。
public static void Main() { Client xiaowang = new Client(); xiaowang.name = "Xiao Wang" xiaowang.age = 27; xiaowang.password = "123456" }
同时小王的年龄不可能是1000
xiaowang.age = 1000
所以对字段公有化的操作,会破坏对象的状态信息,数据安全性和可靠性无法保证。
所以,封装的第一个原则就是将字段定义为private
但是我们对对象的状态信息的控制又该如何实现呢。我们既希望以另外的方式提供给类的外部访问或改变,同时期望实现对数据的访问,因此,加入了另外一个概念,属性。
属性(property)通常定义为public,表示对类的外部成员,属性具有可读、可写的属性,通过get或set访问器来实现读写控制。
public class Client { private string name; public string Name { get { return name; } set { name = value == null ? String.Empty : value; } } private int age; public int Age { get { return age; } set { if ((value>0 && value<150)) { age = value; } else { throw new ArgumentOutOfRangeException("年龄信息不正确");
//异常指定的信息超出了容量的范围 } } }
因此,属性的实质就是在编译时分别将get和set访问器实现为对外方法,从而达到控制属性的目的,对属性的读写行为伴随的实际是一个相应方法的调用。
因此,我们也可以定义自己的get和set访问器。
public string get_Password() { return password; } public string set_Password(string value) { if(value.Length<6){ password = value; } }
这样的方式显然没有实现get和set访问器来得轻便,而且对属性的操作也带来多余的麻烦。下面的这种方式显然更加方便。
public string Password { get {return passowrd;} set { if(value.Length<6){ password = value; } } }
相应的属性只读只需要实现get,属性为只写,只需要实现set.
通过对公共属性的访问来实现对类状态信息的读写控制,1是避免了对数据安全的访问限制,保证了内部数据的可靠性,二是避免了类拓展或者修改所带来的连锁反应。
至于修改变量带来的连锁反应,表现在对类的状态信息的需求信息发生变化时,如何来减少代码重构的基础上实现最小的损失和最大的补救。如client的用户姓名由简单name标识,换成FirstName和secondName
来实现,如果不是属性封装了字段而带来的隐藏内部细节的特点,就要拼命地替换为xiaoWang.name来实现。
private string firstName; private string secondName; public string Name { get { return firstName + secondName; } }
方法封装了类的行为,提供了类的对外表现。用于将封装的内部细节以公有方法提供对外接口,从而实现与外部的交互和相应。
封装的意义:
通过对字段、属性和方法在封装性上这一点的分析。
(1)字段通常为private,属性通常为public.而方法在内部实现为private,对外实现为public.从而保证对内部数据的可靠性读写控制,保证了数据的安全性和可靠性。同时又提供了与外部接口的有效交互。
(2)封装的意义不知类设计层面对字段、属性和方法的控制,更重要的是广义层面,一个系统或软件开发之后,从维护和升级的目的考虑,一定要保证对外几口部分的绝对稳定。不管系统内部的功能性实现如何多变,保证接口稳定一定是保证软件兼容、稳定、健壮的根本。
因此,OO智慧中的封装性旨在保证:
隐藏系统实现的细节,保证系统的安全性和可靠性。
提供稳定不变的对外接口。因此,系统中相对稳定的部分常被抽象为接口。
封装保证了代码模块化,提高了软件的服用和功能分离。
封装的规则:
尽可能调用类的访问器,而不是成员,即使实在类的内部。
内部私有部分可以随意更改,但是一定要保证外部接口稳定的前提下。
对字段的读写控制实现为属性,而不是方法。