随笔编程杂谈录:[-封装-]

作者: e4e52c116681 | 来源:发表于2019-02-16 18:26 被阅读15次

    制造轮子和创造轮子两者的区别在于:一者为复用,一者为封装

    一、与封装的初遇

    现在回到第一次我接触封装的时候:

    两年前,class这个词进入了我的世界,但class并不是我封装思想的启蒙师。
    在此之前,让我初次领略封装的强大之物是电子元件的引脚和它的真值表。
    下面的例子希望你可以好好理解一下:怎么在逻辑上实现一位二进制的加法的逻辑运算单元  
    如果你看不下去,就直接return到第6小点
    

    1.与门(AND)和非门(NOT)

    你觉得很简单?(少年,你对于力量一无所知)
    下面的两个东西的组合是计算机逻辑单元的一切

    与门和非门.png
    ------与门真值表--------------------------非门真值表----------
     输入A  输入B   输出Y                   输入A   输出Y   
     0      0       0                       0       1
     0      1       0                       1       0
     1      0       0                       
     1      1       1     
     -------------------------------------------------------------
     简单解释一下: A和B你可以看成两根导线,只有高电平和低电平两种形式  
     1代表高电平,0代表低电平: 对于A B的每次输入,通过非门都会进行输出,
     真值表就列出了这个元件的所有输入对应输出的情况表
    

    2.一位加法的逻辑设计

    首先我们要明确一位加法的逻辑是什么样的

    0 + 0 = 0     0 + 1 = 1    0 + 1 = 1     1 + 1 = 0 (超出长度1溢出)
    如果将输入A和B看做加数 ,结果看成Y:可以画出下面的真值表:   
    
    --------------------------元件真值表-------------------------
     输入A  输入B   输出Y           
     0      0       0            
     0      1       1            
     1      0       1            
     1      1       0    
     -------------------------------------------------------------
    |--而现在需要做的就是如何通过与门和非门的拼接来形成一个元件
    |--使得这个真值表成立,你可以验证一下上面的四种情况:
    比如 A B 都输入高电压(即1),来校验一下
    上面:非A 得 0 ---> 0 与 B 得 1   --->  取非得 0
                                                    --->0 与 0 得 0  即输出Y是0
    下面:非B 得 0 ---> 0 与 A 得 1   --->  取非得 0
    
    
    1位加法器.png
    这里简单说一下怎么知道要这样画: 
    注:这里为了方便书写,用位运算的符号表述,~ 代表非 &代表与 |代表或
    
    --------------------------元件真值表-------------------------
     输入A  输入B   输出Y           
     0      0       0            
     0      1       1            
     1      0       1            
     1      1       0     
    -------------------------------------------------------------
    [1].找出Y=1的所有行  
    [2].写出上步中的逻辑表达式并用或连接 (~A & B) | ( A & ~B)
    [3].通过与和非替换掉或: 替换公式 X|Y = ~(~X & ~Y)
    ~((~(~A & B)) & (~(A & ~B)))  
    [4].根据与非关系自内向外画图:
    |---上行:~A 即上面的 非门A,然后和 B 一起进入或门 ,结果再取非 即:~(~A & B) 
    |---下行:~B 即下面的 非门B,然后和 A 一起进入或门 ,结果再取非 即:~(A & ~B)
    |---上下两行的结果进入或门,之后再入非门 ~((上行结果) & (下行结果))
    

    3.加法器封装

    说到这里貌似和封装也搭不上啊?但我已经实现了一个逻辑单元
    这个单元可以将两个输入按照1位二进制的逻辑运行,于是封装的价值便体现了
    现在将输入的线连起来之后,再套上一个外壳,它便是一个有逻辑价值独立元件

    1位加法器.png 1位加法器.png
    4.进位器逻辑封装

    首先我们要明确进位的逻辑是什么样的

    逻辑单元:0 + 0 = 0     0 + 1 = 0    1 + 0 = 0     1 + 1 = 1 
    
    --------------------------元件真值表-------------------------         
     输入A  输入B   输出Z           
     0      0       0            
     0      1       0            
     1      0       0            
     1      1       1 
     -------------------------------------------------------------
     
    按照上面的步骤来: A & B  发现原来一个与门就能进行进位操作
    

    现在封装的价值就体现了,也就是轮子的价值,可复用,
    现在将两个元件进行组装,再加个套子,就能实现更复杂些的逻辑处理单元

    加法器.png
    5.小结

    对使用者而言:哥管你里面什么逻辑,我给输入,你给我我想要的输出就行了
    确实一个封装体就做到了,隐藏内部的逻辑实现,将最简洁的使用方式告诉使用者
    下面的一幅图和上面的封装体能完成相同的功能,你更用哪个?

    加法器.png
    --------------元件真值表------------------         
     输入A  输入B    输出Z   输出Y     
     0      0        0       0       
     0      1        0       1       
     1      0        0       1    
     1      1        1       0       
     
    这是将Z,Y两个输出存顺序排列: 0+0=00  0+1=01  1+0=01  1+1=10 
    而这个进一位加法器的逻辑单元已经完善了,就可以当做一个元件来使用
     
    形象而简洁地描述一下:
     在执行  1 + 1 的时候
     高电平经过A,高电平经过B,通过电子元件的内部逻辑单元CRA输出1,通过ADD输出0,
     即 Z输出 1,Y输出 0 ,按Z Y进行输出的到了结果 10  
    

    为了更形象说明,这里拿一个74HC138N看一下,大概三毛钱一个,
    传说中的三八译码器,以前玩单片机做电子钟的时候用到过,现在差不多忘完了...

    IMG20190215193730.jpg 74HC138.png

    在此强调一点:电子元件内部是封装符合真值表的逻辑单元
    电子元件都有输入和输出,即输入+ 逻辑单元处理 ----> 希望的输出
    至此为止,不再延伸,有兴趣的自己玩...


    6. 升华

    我们针对输入输出暴露引脚,将逻辑单元封装其中
    这样对于指输入就会有期望的输出,在便是逻辑单元的封装
    它在软件领域有一个俗称:轮子,这里暂时称为封装体

    封装体的优越之处:(只要封装体能实现预期的功能,那么:)
    |--无论时间,空间的变化,你的输入都会变成你期望的输出
    |--这便具有可复用性,再需要它时便无需再次设计
    |--隐藏内部的逻辑实现,以保护封装体的内部封装不被破坏
    |--仅暴露接口提供输入和输出,简化使用方式
    

    下面关于封装做一个类比:

    -- 电子元件 电脑 开源类库
    封装物 硬质外壳 塑料/金属外壳 .jar,.so包等 躯壳
    接口 引脚 键盘,USB,电源键等 api方法 口 ,耳,眼,鼻,皮肤
    输入 高低电平 键盘输入,U盘头 方法调用 食物,音乐,书籍,气味,触摸
    输出 运算结果 屏幕显示,U盘信息 运算结果 能量,思想,劳动力,汗液等
    处理核心 逻辑单元 CPU 大脑
    使用前提 真值表 说明书 API文档 不可描述(宇宙造物手册?)
    核心组成元 与门+非们 各种电子元件 属性+方法 脱氧核糖核酸

    7.封装没有缺点吗?

    我还没发现世上有什么东西没有缺点

    就连我们这么伟大的人类都可能发生基因突变,更何况是人类制造的封装体  
    
    对于电子元件来说,由于环境因素,人为因素可能导致元件内部的损坏,
    而无法拆封的你只能选择更换一个封装体,这就造成了浪费,虽然在我们眼里不算什么  
    
    对于一个开源框架来说,一个bug可能导致所有使用者的崩溃,这是很严重的  
    也就是使用一个封装体是具有一定的风险性的,当然大厂的框架会相对完善
    
    再者就是接口的复杂,有种必须按照别人意志去做的压迫感  
    没有真值表和接口图,使用的人来说是灾难,这种感觉就像...
    必须使用的类库没有API文档,并且方法命名为a(),b(),c()一样让人抓狂
    可惜对于人类,宇宙并没有留下一份API文档,一切都要靠我们自己来编写...
    

    好了,引入完成,下面进入正文


    二、编程中初遇封装

    1.与class的初遇

    两年前,一开始class 以及它 的 private 是我非常难理解的
    对类的认识是在C++里,印象最深的是圆这个类,从获取圆的面积开始

    ---->[头文件]-------------------
    #ifndef BSAE_CIRCLE_H
    #define BSAE_CIRCLE_H
    class Circle {
        const double PI = 3.141592654;
    public:
        double getArea();
        double getRadius() const;
        void setRadius(double radius);
    private:
        double mRadius;
    };
    #endif //BSAE_CIRCLE_H
    
    ---->[cpp文件]-------------------
    #include "Circle.h"
    double Circle::getArea() {
        return mRadius * mRadius * PI;
    }
    
    double Circle::getRadius() const {
        return mRadius;
    }
    
    void Circle::setRadius(int radius) {
        mRadius = radius;
    }
    
    ---->使用类----------------
    #include <iostream>
    #include "circle/Circle.h"
    
    int main() {
        Circle circle;
        circle.setRadius(10);
        std::cout << circle.getArea() << std::endl;
        return 0;
    }
    
    

    2.现在来看

    一个类便是类的设计者对于某种概念的封装
    我理解类存在的意义确实费了不少时间,当时疑问:
    为什么一行代码解决的事要拆成一个类?而且又是头又是cpp的

    现在发现有这种疑问的根源在于当时没有认清自己的角色  
    认清自己的角色,这对入门的人来说是非常困难的,类的本身就是一个逻辑处理单元
    而程序员的角色是设计类的人,就像电子元件的设计者在设计逻辑单元一样  
    
    但任何一个程序员都必定是第一个使用者,所以两个角色在同一个人身上 
    对于入门的来说,只能是一个使用者,因为你只是在意获取结果,而没有程序员的设计之魂
    就会感觉很混乱,站在一个使用者的角度,类确实将半径为10的圆面积这个问题变得复杂了  
    
    关系.png

    但如果封装的思想到位,就可以明确这个类的价值
    setRadius进行输入,getRadius,getArea进行输出,其中的一切都是逻辑单元

    内部分析.png

    如果你还不清楚,看下图:这就是封装
    看不见内部了,该怎么用? 电子元件有真值表,类有API文档
    至此我想对于类和封装的关系应该讲的淋漓尽致了

    封装.png
    ----------Circle API--------------------------
    double getArea(); 获取圆的面积
    double getRadius(); 获取圆的半径
    void setRadius(double radius); 设置圆的半径
    -----------------------------------------------
    

    当你在非Circle中使用的时候,你的角色就变成了Circle元件的用户
    你需要关注的就只用它的输入和输出,内部逻辑实现已经不需要你考虑了
    不过一旦你进入了Circle中,你的角色就是Creator,你可以把自己当成造物主来实现逻辑单元

    Circle circle;
    circle.setRadius(10);
    circle.getArea()//314.159
    

    相关文章

      网友评论

        本文标题:随笔编程杂谈录:[-封装-]

        本文链接:https://www.haomeiwen.com/subject/xysieqtx.html