.NET 面向对象 之 封装

封装是面向对象语言的起点,如何把现实世界的东西用程序描述出来并实现对应的功能,封装是第一步。封装就是把一些 字段(在类中使用修饰符的变量)、属性(对字段的封装 实质是方法[get,set方法])、方法 包裹在类里,对外屏蔽细节,通过 访问修饰符 来限制。对外公开我们需要公开的东西,隐藏了功能的具体实现,只供简单的使用方法。

提高重用性。

访问修饰符:

private : 私有成员, 在类的内部才可以访问。

protected : 保护成员,该类内部和继承类中可以访问。

internal: 在同一命名空间内可以访问。

protected internal: 该类内部和继承类,并且在同一命名空间内可以访问。

public : 公共成员,完全公开,没有访问限制。

案例代码:

/// <summary>
/// ATM类
/// </summary>
public class ATM
{
    #region 定义私有方法,隐藏具体实现         
    private Client GetUser(long userID)
    {
        Client client = new Client();
        client.Name = "小吴";
        client.Age = 18;
        client.Password = "123456";
        return client;
    }
 
    private bool IsValidUser(Client user)
    {
        return true;
    }
 
    private int GetCash(int money)
    {
        return 0;
    }
    #endregion
 
    #region 定义公有方法,提供对外接口     
    public void CashProcess(long userID, int money)
    {
        Client tmpUser = GetUser(userID);
        if (IsValidUser(tmpUser))
        {
            GetCash(money);
        }
        else
        {
            Console.Write("你不是合法用户,是不是想被发配南极?");
        }
    }
    #endregion
}
 
/// <summary>
/// 用户类
/// get和set对属性的读写控制,是通过实现get和set的组合来实现的,
/// 如果属性为只读,则只实现get访问器即可;
/// 如果属性为可写,则实现set访问器即可。
/// </summary>
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("年龄信息不正确。");
            }
        }
    }
 
    private string password;// 密码      
    public string Password
    {
        get { return password; }
        set
        {
            if (value.Length < 6)
                password = value;
        }
    }
}

创建一个类需要思考的问题:

(1)类的功能是什么?

(2)哪些是字段,哪些是属性,哪些是方法?

(3)对外提供的公有方法有哪些,对内隐藏的私有变量有哪些?

(4)类与类之间的关系是继承还是聚合?

1、封装的第一个原则就是:将字段定义为private。

字段(field)通常定义为private,表示类的状态信息。CLR支持只读和读写字段。值得注意的是,大部分情况下字段都是可读可写的,只读字段只能在构造函数中被赋值,其他方法不能改变只读字段。

public class Client { 
 private string name;// 用户姓名     
 private int age;// 用户年龄    
 private string password;// 用户密码 
}

类的字段信息最好以私有方式提供给类的外部,而不是以公有方式来实现,否则不适当的操作将造成不必要的错误方式,破坏对象的状态信息,数据安全性和可靠性无法保证。

2、属性(property)通常定义为public,表示类的对外成员。

属性具有可读、可写属性,通过get和set访问器来实现其读写控制。

get和set对属性的读写控制,是通过实现get和set的组合来实现的,如果属性为只读,则只实现get访问器即可;如果属性为可写,则实现set访问器即可。

通过对公共属性的访问来实现对类状态信息的读写控制,主要有两点好处:一是避免了对数据安全的访问限制,包含内部数据的可靠性;二是避免了类扩展或者修改带来的变量连锁反应。

至于修改变量带来的连锁反应,表现在对类的状态信息的需求信息发生变化时,如何来减少代码重构基础上,实现最小的损失和最大的补救。例如,如果对client的用户姓名由原来的简单name来标识,换成以firstName和secondName来实现,如果不是属性封装了字段而带来的隐藏内部细节的特点,那么我们在代码中就要拼命地替换原来xiaoWang.name这样的实现了。例如:

private string firstName; 
private string secondName; 
public string Name 
{
 get 
 { 
  return firstName + secondName; 
 } 
}

这样带来的好处是,我们只需要更改属性定义中的实现细节,而原来程序xiaoWang.name这样的实现就不需要做任何修改即可适应新的需求。你看,这就是封装的强大力量使然。

3、方法(method)封装了类的行为,提供了类的对外表现。

用于将封装的内部细节以公有方法提供对外接口,从而实现与外部的交互与响应。例如,从上面属性的分析我们可知,实际上对属性的读写就是通过方法来实现的。因此,对外交互的方法,通常实现为public。

通常将在内部的操作全部以private方式来实现,而将需要与外部交互的方法实现为public,这样既保证了对内部数据的隐藏与保护,又实现了类的对外交互。

谁为公有、谁为私有,取决于需求和设计双重因素,在职责单一原则下为类型设计方法,应该广泛考虑的是类本身的功能性,从开发者与设计者两个角度出发,分清访问权限就会水到渠成。

4、封装小结:

(1)字段通常定义为private,属性通常实现为public,而方法在内部实现为private,对外部实现为public,从而保证对内部数据的可靠性读写控制,保护了数据的安全和可靠,同时又提供了与外部接口的有效交互。这是类得以有效封装的基础机制。

(2)通常情况下的理解正如我们上面提到的规则,但是具体的操作还要根据实际的设计需求而定,例如有些时候将属性实现为private,也将方法实现为private是更好的选择。例如在ATM类中,可能需要提供计数器来记录更新或者选择的次数,而该次数对用户而言是不必要的状态信息,因此只需在ATM类内部实现为private即可;同理,类型中的某些方法是对内部数据的操作,因此也以private方式来提供,从而达到数据安全的目的。

(3)从内存和数据持久性角度上来看,有一个很重要但常常被忽视的事实是,封装属性提供了数据持久化的有效手段。因为,对象的属性和对象一样在内存期间是常驻的,只要对象不被垃圾回收,其属性值也将一直存在,并且记录最近一次对其更改的数据。

(4)在面向对象中,封装的意义还远不止类设计层面对字段、属性和方法的控制,更重要的是其广义层面。我们理解的封装,应该是以实现UI分离为目的的软件设计方法,一个系统或者软件开发之后,从维护和升级的目的考虑,一定要保证对外接口部分的绝对稳定。不管系统内部的功能性实现如何多变,保证接口稳定是保证软件兼容、稳定、健壮的根本。

所以OO智慧中的封装性旨在保证:

1)隐藏系统实现的细节,保证系统的安全性和可靠性。

2)提供稳定不变的对外接口。因此,系统中相对稳定部分常被抽象为接口。

3)封装保证了代码模块化,提高了软件的复用和功能分离。

5、封装规则

(1)尽可能地调用类的访问器,而不是成员,即使在类的内部。其目的在我们的示例中已有说明,例如Client类中的Name属性就可以避免由于需求变化带来的代码更改问题。

(2)内部私有部分可以任意更改,但是一定要在保证外部接口稳定的前提下。

(3)将对字段的读写控制实现为属性,而不是方法,否则舍近而求远,非明智之选。

(4)类封装是由访问权限来保证的,对内实现为private,对外实现为public。再结合继承特性,还要对protected,internal有较深的理解。

(5)封装的精华是封装变化。张逸在《软件设计精要与模式》一书中指出,封装变化是面向对象思想的核心,他提到开发者应从设计角度和使用角度两方面来分析封装。因此,我们将系统中变化频繁的部分封装为独立的部分,这种隔离选择有利于充分的软件复用和系统柔性。

结论:

封装是什么?封装就是一个包装,将包装的内外分为两个空间,对内实现数据私有,对外实现方法调用,保证了数据的完整性和安全性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值