精华内容
下载资源
问答
  • 走进信息隐藏的世界,全面讲解信息隐藏——第1节:信息隐藏技术简介 专栏题记:奥斯卡优秀电影《美丽心灵》里面有讲述一位优秀数学家为政府破译敌国通讯的情节,如电影所讲,现实中也有着类似的情节,在我们的生活...

    走进信息隐藏的世界,全面讲解信息隐藏——第1节:信息隐藏技术简介


    专栏题记:奥斯卡优秀电影《美丽心灵》里面有讲述一位优秀数学家为政府破译敌国通讯的情节,如电影所讲,现实中也有着类似的情节,在我们的生活中,我们所看过的图片、视频和游览过的网页,也许就隐藏着他人需要传输的秘密信息(是不是有点恐怖,差一点成为帮凶了),这就是信息隐藏!由于应用场景一般比较神秘,导致很多人其实都不太了解信息隐藏技术。由于国家越来越重视网络信息的安全,现在也是有越来越多的研究人员进行着这方面的研究工作。本人也是其中一位非常平凡的研究人员,笔者抱着学习和分享的态度,希望略尽绵薄之力让大家对信息隐藏技术不再陌生,同时慢慢掌握如何进行信息隐藏的技术。接下来本人会不定时更新信息隐藏技术的相关原理和关键技术实现过程,同时会提供程序代码给大家学习,也欢迎有志之士可以和我一同创建学习平台,也可以在评论下建议下一节希望学习的研究内容。谢谢!


    目录

    1、信息隐藏概念

    2、信息隐藏系统模型

    3、信息隐藏技术的分支简介

    4、信息隐藏技术的特性和要求

    5、信息隐藏关键技术

    6、信息隐藏的应用实例

    案例一:信息隐藏技术在电子商务中的应用

    案例二:信息隐藏技术在网络战中的运用



    • 信息隐藏概念

           信息隐藏也称数据隐藏,信息隐藏技术是指在不对载体数据产生可察觉影响的前提下,将密信数据隐藏到载体中实现隐蔽通讯的技术。是集多学科理论与技术于一身的新兴技术领域。信息隐藏技术主要是指将特定的信息嵌入数字化宿主信息(如文本,数字化的声音、图像、视频信号等)中,信息隐藏的目的不在于限制正常的信息存取和访问,而在于保证隐藏的信息不引起监控者的注意和重视,从而减少被攻击的可能性,在此基础上再使用密码术来加强隐藏信息的安全性,因此信息隐藏比信息加密更为安全。应该注意到,密码术和信息隐藏技术不是互相矛盾、互相竞争的技术,而是相互补充的技术,他们的区别在于应用的场合不同,对算法的要求不同,但可能在实际应用中需要互相配合。特定的信息一般就是保密信息,信息隐藏的历史可以追溯到古老的隐写术,但推动了信息隐藏的理论和技术研究始于1996年在剑桥大学召开的国际第一届信息隐藏研究会,之后国际机构在信息隐藏领域中的隐写术、数字水印、版权标识,可视密码学等方面取得大量成果。


    • 信息隐藏系统模型

           广义的信息隐藏系统模型主要有四部分组成:(1)信息嵌入,即利用嵌入秘钥来实现嵌入对象的隐藏过程;(2)信息提取,即利用提取秘钥从隐藏对象或可能经过修改的隐藏对象中提取或恢复出嵌入对象,在提取时,原始的载体对象可能需要参与也可能不需要参与;(3)秘钥生成,根据安全参数生成嵌入秘钥和提取秘钥;(4)隐藏分析,隐藏对象在传输过程中可能会被隐藏分析者截获并进行处理。信息隐藏系统模型如下图所示:

           在信息隐藏系统模型中,在嵌入过程中我们使用嵌入密钥将嵌入对象嵌入掩护对象中,生成隐藏对象,如下图将一个txt的文本嵌入到一张JPEG的图像中。嵌入对象和掩护对象可以是文本、图像或音频等等。在我们没有使用工具进行分析时,我们觉得掩护对象与隐藏对象几乎没有差别,这就是信息隐藏概念中所说的“利用人类感觉器官的不敏感性”。隐藏对象在信道中进行传输,在传输的过程中,有可能会遭到隐藏分析者的攻击,隐藏分析者的目标在于检测出隐藏对象、查明被嵌入对象、向第三方证明消息被嵌入、删除被嵌入对象、阻拦等。其中前三个目标通常可以由被动观察完成,称为被动攻击,后两个目标通常可以由主动攻击实现。提取过程则是在提取密钥的参与下从所接收到的隐藏对象中提取出嵌入对象,如将上述txt文件从JPG的图像中提取出来。有些提取过程并不需要掩护对象的参与,这样的系统称为盲隐藏技术,而需要掩护对象参与的系统则称为非盲隐藏技术。


    • 信息隐藏技术的分支简介

           作为信息安全领域的一个重要组成部分,信息隐藏技术已成为信息安全领域中一个既具有研究价值、同时又极具挑战性的热门课题,信息隐藏技术的分支主要包括:隐写术、数字水印、数字指纹、隐蔽信道、阈下信道、低截获概率通信和匿名通信等等。其分类示意图如下图所示:


    • 信息隐藏技术的特性和要求

           信息隐藏不同于传统的加密,因为其目的不在于限制正常的资料存取,而在于保证隐藏数据不被发现。因此,信息隐藏技术必须考虑正常的信息操作所造成的威胁,即要使机密数据对正常的数据操作技术具有免疫力。根据信息隐藏的不同目的和技术要求,该技术的存在以下特性和要求:

    1. 透明性或不可感知性:利用人类视觉系统或人类听觉系统属性,经过一系列的隐藏处理,使得载体对象没有明显的降质现象,如LSB算法等。当然,有些场合可能需要使用可见水印,例如某些版权维护的场合。
    2. 鲁棒性:指不因隐藏对象通过某些常用操作而导致嵌入对象丢失的能力。这里的常用操作包括滤波操作、有损压缩、几何变换、D/A或A/D等。
    3. 安全性:指算法具有较强的抗恶意攻击能力。
    4. 不可检测性:指载体数据嵌入数据后无明显改变,至少肉眼看不出变化

    • 信息隐藏关键技术

      近年来,信息隐藏技术的研究取得了很大的进步,已经提出了各种各样的隐藏算法。关键的信息隐藏技术有如下几种。

        (1)替换技术

        所谓替换技术,就是试图用秘密信息比特替换掉伪装载体中不重要的部分,以达到对秘密信息进行编码的目的。替换技术包括最低比特位替换、伪随机替换、载体区域的奇偶校验位替换和基于调色板的图像替换等。替换技术是在空间域进行的一种操作,通过选择合适的伪装载体和适当的嵌入区域,能够有效地嵌入秘密信息比特,同时又可保证数据的失真度在人的视觉允许范围内。

        已经提出的各种算法大都给出了其实现思想,如对于基于调色板的图像格式,可操作其调色板来嵌入信息,也可以利用它的量化值来隐藏秘密信息,因此该技术在数据伪装中得到了广泛的应用。

        替换技术算法简单,容易实现,但是鲁棒性很差,不能抵抗图像尺寸变化、压缩等一些基本的攻击,因此在数字水印领域中一般很少使用。

        (2)变换技术

        大部分信息隐藏算法都是在变换域进行的,其变换技术包括离散傅里叶变换(DFT)、离散余弦变换(DFT)、离散小波变换(DWT)和离散哈达玛特变换(DHT)等。这些变换技术都有各自的特点。

        DFT在信号处理中有着广泛应用,在信息隐藏领域也同样得到了应用。它将图像分割成多个感觉频段,然后选择合适部分来嵌入秘密信息。D CT使空间域的能量重新分布,从而降低了图像的相关性。在DCT域中嵌入信息的方法,通常是在一个图像块中调整两个(或多个)DCT系数的相对大小。DWT是对图像的一种多尺度、空间频率分解,即将输入信号分解为低分辨率参考信号和一系列细节信号。在一个尺度下,参考信号和细节信号包含了完全恢复上一尺度下信号的全部信息。

        (3)扩频技术

        当对伪装对象进行过滤操作时可能会消除秘密信息,解决的方法就是重复编码,即扩展隐藏信息。在整个伪装载体中多次嵌入一个比特,使得隐藏信息在过滤后仍能保留下来,这种方法虽然传输效率不高,但却具有较好的健壮性。扩频技术一般是使用比发送的信息数据速率高许多倍的伪随机码,将载有信息数据的基带信号频谱进行扩展,形成宽带低功率谱密度信号。最典型的扩频技术,为直序扩频和跳频扩频。直序扩频是在发端直接用具有高码率的扩频编码去扩展信号的频谱,而在接收端用相同的扩频编码解扩,将扩频信号还原为原始信号。跳频扩频是在发端将信息码序列与扩频码序列组合,然后按照不同的码字去控制频率合成器,使输出频率根据码字的改变而改变,形成频率的跳变;在接收端为了解跳频信号,要用与发端完全相同的本地扩频码发生器去控制本地频率合成器,从中恢复出原始信息


    • 信息隐藏的应用实例

    案例一:信息隐藏技术在电子商务中的应用

      目前信息隐藏技术在电子商务中的应用主要体现在以下几个方面:

      1.数据保密

      在具体电子商务活动中,数据在Internet上进行传输一定要防止非授权用户截获并使用,如敏感信息,谈判双方的秘密协议合同网上银行交易中的敏感数据信息,重要文件的数字签名和个人隐私等等。另外,还可以对一些不愿为别人所知道的内容使用信息隐藏的方式进行隐藏存储。

      2.数据的不可抵赖性

      在网上交易中,交易双方的任何一方不能抵赖自己曾经做出的行为,也不能否认曾经接收到的对方的信息,这是交易系统中的一个重要环节。这可以使用信息隐藏技术中的水印技术,在交易体系的任何一方发送或接收信息时,将各自的特征标记以水印的形式加入到传递的信息中,这咱水印应是不能被去除的,可达到确认其行为的目的。

      3.防伪

      商务活动中的各种票据的防伪也是信息隐藏技术的用武之地。在数字票据中隐藏的水印经过打印后仍然存在,可以通过再扫描回数字形式,提取防伪水印,以证实票据的真实性。

      4.数据的完整性

      对于数据完整性的验证是要确认数据在网上传输或存储过程中并没有被窜改,可通过使用脆弱水印技术保护的媒体一旦被窜改就会破坏水印,从而很容易被识别。

    案例二:信息隐藏技术在网络战中的运用[2]

      信息隐藏之所以比密码加密的方法进行保密通信具有更大优势,是因为以信息隐藏方式实现隐蔽通信,除通信双方以外的任何第三方并不知道秘密通信这个事实的存在,这就较之单纯的密码加密更多了一层保护,使得网络加密机制从“看不懂”变为“看不见”,以不至成为好事者攻击的目标。

      (1)数据保密

      在因特网上防止非授权用户截获并使用传输的一些秘密数据,是网络安全的一个重要内容。信息隐藏技术在军事上的应用,可以将一些不愿为人所知的重要标识信息用信息隐藏的方式进行隐蔽存储,像军事地图中标明的军备部署、打击目标,卫星遥感图像的拍摄日期、经纬度等等,都可用隐藏标记的方法使其以不可见的形式隐藏起来,只有掌握识别软件的人才能读出标记所在。

      (2)数据保护

      数据保护主要是保证传输信息的完整性。由于隐藏的信息是被藏在宿主图像等媒体的内容中,而不是文件头等处,因而不会因格式的变换而遭到破坏。同时隐藏的信息具有很强的对抗非法探测和非法破解的能力,可以对数据起到安全保护的作用。对于数据完整性的验证是要确认数据在网上传输或存储过程中并没有被窜改。通过使用脆弱水印技术保护的媒体一旦被窜改就会破坏水印,从而很容易被识别。

      (3)数据免疫

      所谓免疫是指不因宿主文件经历了某些变化或处理而导致隐藏信息丢失的能力。某些变化和处理包括:传输过程中的信道噪声干扰,过滤操作,再取样,再量化,数/模、模/数转换,无损、有损压缩,剪切,位移等。


    作者:Daniel
    来源:CSDN 
    版权声明:本文为原创文章,转载请附上博文链接:
    https://blog.csdn.net/qq_26464039/article/details/85779870


    展开全文
  • 在整个介绍信息隐秘技术部分, 为了统一起见, 我们约定以下名词: 称需要隐秘的信息为秘密信息( secret) , 秘密信息隐藏的媒介叫做载体( cover) , 隐藏后的结果叫做隐蔽载体( stego-cover) 一、图...

    从本章开始, 我们将具体接触到许多数字信息的隐写术。为了与后面数字水印技术相区别, 我们也将隐写术称为信息隐秘技术。本章主要阐述图像信息作为秘密信息的隐藏, 即图像降级隐写。
    在整个介绍信息隐秘技术部分, 为了统一起见, 我们约定以下名词: 称需要隐秘的信息为秘密信息( secret) , 秘密信息隐藏的媒介叫做载体( cover) , 隐藏后的结果叫做隐蔽载体( stego-cover)
    一、图像降级
    主体对客体的读写一般应满足以下两个规则:
    规则 1: 主体只能向下读, 不能向上读。
    规则 2: 主体只能向上写, 不能向下写。
    我们通常所说的信息降级, 就是通过将秘密信息嵌入较低安全级别的客体中, 破坏了第二个规则, 从而使机密的信息看上去不再机密。在伪装技术中我们经常要这样做, 以使秘密信息被伪装成为低级别的信息, 不易被发现。图像降级就是伪装技术的一个应用。
    二、简单的图像信息伪装技术
    1.直接 4bit 替换法
    这是将秘密图像信息嵌入载体图像的一种最简单的方法,指直接用秘密图像像素值的高 4bit 去替换载体图像像素值的低 4bit。

    % 文件名: imagehide. m
    % 函数功能: 直接将秘密图像的高 4bit 隐藏在 RGB 载体图像的 R, G, B 层中所
    选的那一层的低 4bit, 并将秘密图像提取出来, 最后显示。要求载体图像的大小大于
    等于秘密图像的大小, 且秘密图像是二值或灰度图像
    % 输入格式:
    % data = imagehide( ′c: \ lenna. bmp′, ′c: \ woman. bmp′, ′c: \mix. bmp′, ′bmp′, 3 )
    % 参数说明
    % cover 是载体图像的地址
    % message 是秘密图像的地址
    % goleimage 是隐藏后图像的地址
    % permission 是图像的类型
    % level 是作为载体的具体层, R 为 1 , G 为 2, B 为 3
    % data 是隐藏后图像的矩阵
    
    function data = imagehide( cover, message, goleimage, permission, level)
    % 提取图像信息并分层
    cover = imread( cover, permission) ;
    data = cover;
    msg = imread( message, permission) ;
    [ row, col] = size( cover) ;
    cover1 = cover(∶,∶, level) ;
    % 置载体图像 R 层的低 4bit 为 0
    for i = 1∶row
    		for j = 1∶col /3
    				cover1( i, j) = bitand( cover1( i, j) , 240) ;
    		end
    end
    % 置秘密图像的低 4bit 为 0
    takemsg4 = bitand( msg, 240) ;
    % 将秘密图像的高 4bit 右移 4 位
    shiftmsg4 = bitshift( takemsg4, - 4) ;
    % 图像隐藏
    for i = 1∶row
    		for j = 1∶col /3
    				cover1( i, j) = bitor( cover1( i, j) , shiftmsg4( i, j) ) ;
    		end
    end
    % 写回并保存
    data(∶,∶, level) = cover1;
    imwrite( data, goleimage, permission) ;
    % 提取秘密图像信息, 检测隐藏效果
    data = imread( goleimage, permission) ;
    [ row, col] = size( data) ;
    A = data(∶,∶, level) ;
    for i = 1∶row
           for j = 1∶col /3
    				A( i, j) = bitand( A( i, j) , 15) ;
           end
    end
    A = bitshift( A, 4) ;
    % 显示结果
    subplot( 221) , imshow( cover) ; title( ′载体图像′) ;
    subplot( 222) , imshow( message) ; title( ′秘密图像′) ;
    subplot( 223) , imshow( data) ; title( ′隐藏后的图像′) ;
    subplot( 224) , imshow( A) ; title( ′提取的秘密图像′) ;
    

    但无论选择R G B三层中的哪层嵌入,都会在不同程度上对原始图像造成破坏
    考虑到第一章中我们阐述的 RGB 颜色模型, 将秘密图像隐藏在一层中, 容易导致该点的色彩向相应的坐标上发生绝对偏移, 从而使得该像素点的色彩的相应分量突出。所以, 我们不能笼统地认为图像隐藏在某层比较好而隐藏在某层不好, 这是因为对于具体的某个像素点其哪个颜色分量突出是不确定的。但是, 我们可以通过改进算法来限制这种颜色沿相应坐标的绝对偏移。

    例如, 可将秘密图像像素值的高 4bit 分别藏于载体图像 R, G, B 层像素值的最低位或次低位, 即将秘密图像的高 2bit 藏于 R 层, 另外 2bit 分别藏于 G 层和 B 层, 此时像素色彩的改变就不是沿一个坐标方向而改变, 而是在整个 RGB 空间中发生偏移, 改变后的颜色坐标点与改变前的颜色坐标点的距离( 数学上的范数) 比单纯在一个分量上改变的两点距离要小, 这样对载体图像的影响会更小。在实际应用中, 还应该考虑隐藏的鲁棒性等问题。

    2.对第 4bit 的考察
    可以发现,直接替换 4 bit 后, 图像还是有一些变化的, 也就是说, 替换容量大使图像的保真度降低( 可通过实验看到替换 3bit 的效果比替换 4bit 的效果要好)
    在这种情况下,我们可以用秘密信息图像像素值的高3bit去替换载体图像像素值的低3bit,至于第 4bit 则要具体分析
    其假设前提是: 如果只对图像进行 3bit 替换, 是不会对图像的视觉效果造成影响的。事实上, 这种假设是可以成立的

    首先, 我们引入一个相似度的概念, 所谓相似度, 是指两图像块中同一坐标下的像素中第 4bit 相同的像素数量占一块图像全部像素的比例, 表示为:μ=s/64
    其中 s 为第 4bit 相同的像素数量 64 为 8× 8 块中的总像素数
    根据 μ的取值我们来确定该块各像素第 4bit 的隐藏策略。
    我们先计算相应块的载体图像与秘密图像在第 4bit 的相似度 μ, 如果 μ大于某一阈值 T, 则可直接用秘密图像的第 4 bit 替换载体图像的第 4bit, 如果 μ小于阈值1 - T, 则先将秘密图像的第 4bit 取反后再替换, 若 μ介于 1 - T 和 T 之间, 则不进行替换。当然, 要用一个替换表对第 4bit 进行替换或取反替换了的块进行记录, 并且将此表也嵌入到载体图像中。编写函数 fourthbitcmp. m 完成记录替换表的实验, 函数代码如下:

    % 文件名: fourthbitcmp. m
    % 函数功能: 计算秘密图像和选择的载体图像层, 对于第 4bit 的每一个 8× 8 块,哪些可以用秘密图像去替换载体图像, 并返回一个替换表 count, 要求两个图像都可以整数 8× 8 分块
    % 输入格式: count = fourthbitcmp( ′c: \lenna. bmp′, ′c: \woman. bmp′, ′bmp′, 3, 0. 7)
    % 参数说明:
    % cover 是载体图像的地址
    % message 是秘密图像的地址
    % permission 是图像的类型
    % level 是作为载体的具体层。R 为 1, G 为 2 , B 为 3
    % count 是替换表
    % threshold 是阈值
    function count = fourthbitcmp( cover, message, permission, level, threshold)
    % 提取图像信息并分层
    cover = imread( cover, permission) ;
    data = cover;
    msg = imread( message, permission) ;
    cover1 = cover(∶,∶, level) ;
    % 对 cover 和 msg 的第 4bit 进行处理
    tempc = cover1;
    tempm = msg;
    tempc = bitand( tempc, 8) ;
    tempm = bitand( tempm, 8) ;
    temp = bitxor( tempm, tempc) ;
    [ row, col] = size( temp) ;
    % 记录图像每个分块的 n 值
    k1 = 0;
    k2 = 0;
    a = row* col /64;
    count = zeros( [ 1 a] ) ;
    for i = 1∶a
    	for m = 1∶8
    		for n = 1∶8
    				if temp( 8* k1 + m, 8* k2 + n) == 0
    					count( 1, i) = count( 1, i) + 1;
    				end
    		end
    	end
    	k2 = k2 + 1;
    	if k2* 8 == col
    			k2 = 0;
    					k1 = k1 + 1;
    			end
    	end
    % 计算每块的 μ值并与阈值进行比较
    count = count /64;
    for i = 1∶a
    	if count( i) >= threshold
    		count( i) = 1; % 可以替换
    			elseif count( i) < 1 - threshold
    				count( i) = - 1; % 取反
    			else
    		        count( i) = 0; % 不能处理
    	end
    end
    

    依据本算法, 在同一阈值下经不同层计算出的替换表中 0 的个数, 个数越少的层越适宜当做载体。当然, 为了简单起见, 也可以不加分块直接计算秘密图像与载体图像 R、G、B 层中哪一层的相似度高, 就选择哪一层为载体。
    三、图像置乱
    置乱实际上就是图像的加密, 与加密保证安全性不同的是, 将置乱的图像作为秘密信息再进行隐藏, 可以很大限度地提高隐蔽载体的鲁棒性, 所以图像置乱是信息隐藏中常用的一项技术。
    1、变化模板形状的图像置乱算法
    变化模板形状的图像置乱算法的思想如下:
    ① 对原图像取一个固定模板, 模板中像素位置排列如图 4. 10 所示。
    ② 做一个与原图像模板不同的置乱模板, 如图 4. 11 所示, 在置乱模板中把图像模板中的像素位置按一定次序填入
    ③ 将置乱模板中的像素位置再按一定的次序填回到原图像模板中就得到了置乱后的图像模板( 图 4. 12 的模板是按从左到右、从上到下的次序依次读取置乱模板中像素位置) 。
    在这里插入图片描述
    可以发现, 这种置乱算法是对合的
    与前面 Zigzag 变换一样, 我们也采取查表的方法编写程序。由于我们固定了置乱模板的大小, 所以在对图像置乱前我们要对其进行边界修补。如取置乱模板为 32× 32, 则要求秘密图像的尺寸为 32× 32,64× 64 , 128× 128, …。假设一幅图像的尺寸为 32× 31 , 则应该对其增加 1 列数据。
    变换表分为行表和列表, 同一坐标下的行列表中的数据结合起来所指示的像素将被置乱到这一坐标下。
    此外, 在图像置乱机制中引入一个简单的密钥控制。将由密钥生成的第一个[ 128, 255] 的随机整数与置乱的结果进行模 2 加。编写程序 diamondreplace. m 完成置乱实验。其中需要调用查表程序 replace32 fun. m, 函数代码如下:
    ( 1 ) 主函数: diamondreplace. m

    % 文件名: diamondreplace. m
    % 函数功能: 本函数将完成对输入的图像信号按菱形置换策略进行置乱
    % 输入格式举例: result = diamondreplace( secretimage, 1983 )
    % 参数说明:
    % matrix 为输入图像矩阵
    % key 为控制密钥
    % result 为置乱后的结果
    function result = diamondreplace( matrix, key)
    % 分析原图像尺寸并补遗
    [ m, n] = size( matrix) ;
    rowadd = 32-mod( m, 32) ;
    coladd = 32-mod( n, 32 ) ;
    if rowadd== 32
    rowadd = 0;
    end
    if coladd== 32
    coladd = 0;
    end
    input = uint8( zeros( [ m + rowadd n + coladd] ) ) ;
    input( 1∶m, 1∶n) = matrix;
    % 密钥生成随机数
    rand( ′seed′, key) ;
    control = randint( 1, 1 , [ 128 255] ) ;
    % 查表置乱
    fun = @replace32fun; % 调用子函数
    result = blkproc( input, [ 32 32] , fun) ;
    result = bitxor( result, control( 1 , 1) ) ;
    

    ( 2 ) 查表函数: replace32fun. m

    function result = replace32fun( matrix)
    % 行转换表
    row = [ 16 15 17 14 16 18 …] % 此处略去, 具体内容请见表 4. 3
    col = [ 17 18 17 20 18 16 …] % 此处略去, 具体内容请见表 4 . 4
    for i = 1∶32
    for j = 1∶32
    result( i, j) = matrix( row( i, j) , col( i, j) ) ;
    end
    end
    

    woman图像置乱后的效果
    将图像的原始信息破坏得越大越好, 不过, 这种破坏一定要是可以复原的

    2.图像的幻方变换

    % 文件名: magicsquares. m
    % 函数功能: 本函数将完成 n 阶二维幻方的求取 . 要求 n 为奇数
    % 输入格式举例: result = magicsquares( 5)
    % 参数说明:
    % n 为阶数
    % result 为求得的二维幻方
    function result = magicsquares( n)
    if mod( n, 2) == 0
    error( ′n 要求为奇数′) ;
    end
    result = zeros( n) ;
    j = floor( n /2 ) + 1; % 中间 1 列
    i = n + 1; % 便于以后从第 n 行开始考虑起
    result( 1, j) = 1 ;
    for k = 2∶n* n % 依次考虑后 n^2 - 1 个数
    i = i - 1 ;
    j = j + 1 ; % 行数减 1, 列数加 1
    if i < 1 && j > n % 特殊情况 4
    i = i + 2 ;
    j = j - 1 ;
    else
    if i < 1 % 特殊情况 1
    i = n;
    end
    if j > n % 特殊情况 2
    j = 1;
    end;
    end;
    if result( i, j) == 0
    result( i, j) = k;
    else % 特殊情况 3
    i = i + 2 ;
    j = j - 1 ;
    result( i, j) = k;
    end
    end
    
    

    我们知道, 要图像置乱是为了增加隐藏的鲁棒性。一个置乱的图像无论是合法用户还是非法用户都看不懂, 要使合法用户能完整地读取秘密信息, 就要满足两个条件:
    ①仅有合法用户知道的密钥参与运算。
    ②置乱算法本身可以保证图像复原, 即算法是可逆的。
    幻方置乱的思想其实也是查表思想,其运算具有准对合性,具体算法实现后面再阐述
    编 写 函 数 magicreplace. m 完 成 置 乱 实 验, 其 中 需 要 调 用 查 表 函 数replacemagicfun. m。前主函数存放 11 阶标准幻方, 子函数存放表 4. 6 , 函数代码如下:
    ( 1 ) 幻方置乱主函数: magicreplace.m

    % 文件名: magicreplace. m
    % 函数功能: 本函数将完成对输入的图像信号按幻方置换策略进行置乱
    % 输入格式举例: result = magicreplace( secretimage, 1, 1983)
    % 参数说明:
    % matrix 为输入图像矩阵
    % key 为控制密钥
    % eord 为 1 表示置乱变换, 为 0 表示复原变换
    % result 为置乱后的结果
    function result = magicreplace( matrix, eord, key)
    % 分析原图像尺寸并补遗
    [ m, n] = size( matrix) ;
    rowadd = 11-mod( m, 11) ;
    coladd = 11-mod( n, 11 ) ;
    if rowadd == 11
    rowadd = 0;
    end
    if coladd == 11
    coladd = 0;
    end
    input = uint8( zeros( [ m + rowadd n + coladd] ) ) ;
    input( 1∶m, 1∶n) = matrix;
    % 密钥生成随机数
    rand( ′seed′, key) ;
    control = randint( 1, 1 , [ 1 121] ) ;
    % 11 阶标准幻方
    magic =
    [ 38 52 66 69 83 97 100 114 7 21 24 
      61 75 78 92 106 120 2 16 30 44 47
      84 98 101 115 8 22 25 39 53 56 70 
      107 121 3 17 31 34 48 62 76 79 93 
      9 12 26 40 54 57 71 85 99 102 116
      32 35 49 63 77 80 94 108 111 4 18 
      55 58 72 86 89 103 117 10 13 27 41
      67 81 95 109 112 5 19 33 36 50 64
      90 104 118 11 14 28 42 45 59 73 87
      113 6 20 23 37 51 65 68 82 96 110 
      15 29 43 46 60 74 88 91 105 119 1 ] ; 
    if eord == 0
    control = 121 -control;
    elseif eord == 1
    control = control;
    else
    error( ′输入参数错误′) ;
    end
    % 幻方变换主过程
    for define = 1∶key% control
    	for r = 1∶11
    		for c = 1∶11
    			magic( r, c) = magic( r, c) -1;
    			if magic( r, c) == 0
    					magic( r, c) = 121;
    			end
    		end
    	end
    end
    % 查表置乱
    fun = @replacemagicfun; % 调用子函数
    result = blkproc( input, [ 11 11] , fun, magic) ;
    

    ( 2 ) 行列转换表子函数: replacemagicfun. m

    % 11 阶幻方的行列查找表程序
    function result = replacemagicfun( matrix, P1 )
    % 初始化 11 阶幻方的行列查找表
    row = [ 11 , 2, 4, 6, 8, 10, 1, 3, 5, 7, 9, 5, 7, 9, 11, 2, 4, 6 , 8, 10, 1 , 3, 10, 1 , 3, 5, 7, 9,
    11, 2, 4, 6 , 8, 4, 6, 8, 10, 1, 3, 5, 7, 9 , 11, 2 , 9 , 11, 2, 4, 6, 8 , 10, 1, 3, 5 , 7, 3, 5, 7 , 9, 11 , 2,
    4 , 6 , 8, 10 , 1, 8, 10 , 1, 3, 5, 7, 9, 11, 2, 4, 6 , 2 , 4 , 6, 8, 10, 1 , 3, 5, 7, 9, 11, 7, 9, 11, 2, 4 , 6,
    8 , 10, 1, 3 , 5, 1, 3, 5, 7, 9, 11, 2, 4, 6 , 8, 10 , 6 , 8, 10 , 1, 3, 5, 7, 9, 11, 3, 4] ;
    col = [ 11, 7, 3, 10, 6, 2, 9, 5, 1, 8, 4 , 2, 9, 5, 1, 8, 4, 11, 7, 3 , 10, 6, 4, 11, 7 , 3, 10 , 6,
    2 , 9 , 5, 1, 8, 6, 2, 9, 5 , 1, 8, 4, 11, 7, 3, 10 , 8, 4, 11, 7 , 3, 10, 6, 2, 9, 5 , 1, 10, 6, 2 , 9, 5, 1,
    8 , 4 , 11, 7 , 3, 1, 8, 4, 11, 7, 3, 10, 6, 2, 9, 5 , 3 , 10, 6, 2, 9, 5 , 1, 8, 4, 11 , 7, 5, 1, 8 , 4, 11 , 7,
    3 , 10, 6, 2 , 9, 7, 3, 10, 6, 2, 9, 5, 1, 8 , 4, 11 , 9 , 5, 1, 8, 4, 11, 7, 3, 10, 6, 2] ;
    for i = 1: 11
    	for j = 1: 11
    		result( i, j) = matrix( row( P1 ( i, j) ) , col( P1( i, j) ) ) ;
    	end
    end
    

    3.图像的 Ha sh 置乱
    前面的两种置乱都是对图像分块进行的, 而且其共同的问题是密钥控制并不得力。下面介绍的一种图像置乱方法实际上就是我们在 2. 6. 3 节中介绍的 Hash 置换的特例———对于 m× n 个像素点, 我们要求随机置换 m× n 个, 就完成了图像的 Hash置乱。鉴于该算法具有无冲突( collision) 和强密钥控制的特点, 显然是一个很好的图像置乱算法。需要说明的是, 这种算法不是对合的, 所以在实现上较前两种复杂一些。另外, 其算法执行起来也比较费时间。编写函数 hashdisturb. m 完成实验

    % 文件名: hashdisturb. m
    % 函数功能: 本函数将完成对输入的图像信号按 Hash 置换策略进行置乱
    % 输入格式举例: result = hashdisturb( secretimage, 1, 1983 , 421, 1121)
    % 参数说明:
    % matrix 为输入图像矩阵
    % key1 -key3 为控制密钥
    % eord 为 1 表示置乱变换, 为 0 表示复原变换
    % result 为置乱后的结果
    function result = hashdisturb( matrix, eord, key1, key2, key3 )
    % 分析原图像尺寸并补遗
    [ m, n] = size( matrix) ;
    % 调用随机置换函数
    [ row, col] = hashreplacement( matrix, m* n, key1, key2, key3) ;
    % 置乱函数
    count = 1;
    if eord == 1
    	for i = 1∶m
    		for j = 1∶n
    			result( i, j) = matrix( row( count) , col( count) ) ;
    			count = count + 1;
    		end
    	end
    end
    % 复原函数
    if eord == 0
    	for i = 1∶m
    		for j = 1∶n
    			result( row( count) , col( count) ) = matrix( i, j) ;
    			count = count + 1;
    		end
    	end
    end
    
    

    4.隐藏置乱图像的优点
    置乱图像隐藏的抗恶意攻击性能
    经过多次置乱后, 图像就会彻底地改变, 从置乱后的图像上根本看不到原图像的任何特征。使用置乱方法为什么可以增加图像伪装的鲁棒性呢?
    首先, 将图像置乱后, 将得到一幅杂乱无章的图像, 这个图像无色彩、无纹理、无形状, 从中无法读取任何信息, 那么, 将这样一幅图嵌入到另一幅普通图像时就不易引起那幅图色彩、纹理、形状的太大改变, 甚至不会发生改变, 这样人眼就不易识别,从而逃出了第三方的视线。

    其次, 由于秘密图像是置乱后的图像, 根据上述图像的“三无”特征, 第三方根本不可能对其进行色彩、纹理、形状等的统计分析, 即便他们截取到了秘密图像, 也是无能为力的。如果第三者企图对秘密图像进行反置乱, 这也是不可能的, 由于图像置乱有很多种方法, 每种方法又可以使用不同的置乱模板算法, 设置不同的参数, 使用者有很大的自由度, 他可以根据自己的想法得到不同的结果, 相反, 这给企图截获秘密信息的第三方带来了很大的困难, 使他们需要耗费巨大的计算量来穷举测试各种可能性

    最后, 我们再设想一下, 如果第三方反置乱不成, 在隐蔽载体上恶意修改怎么办?通过实验我们知道, 用置乱的方法是可以抵抗这些攻击的, 因为对秘密图像进行反置换的过程, 就使得第三方在图像上所涂、画的信息分散到画面的各个地方, 形成了点状的随机噪声, 对视觉影响的程度不大。图 4. 20 是我们随意对隐蔽载体进行 3 种恶意攻击后提取的秘密图像内容。可以看到, 即使是在攻击 3 下, 秘密图像的轮廓依然可见, 这是在未置乱图像的隐藏下不可想像的。当然, 为了使提取的信息更为清晰,最好对破坏严重的图像进行边界保持的中值滤波等方面的处理, 以去除随机噪声。

    展开全文
  • C++ 封装 & 信息隐藏

    千次阅读 多人点赞 2021-05-02 14:02:39
    C++ 封装 & 信息隐藏简介

    概述

    封装是面向对象编程中的把数据和操作数据的函数绑定在一起的一个概念. 这样能避免受到外界干扰和误用.
    在这里插入图片描述
    数据隐藏包括数据封装和数据抽象两部分. 数据封装是一种把数据和操作数据的函数捆绑在一起的机制. 数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制.
    在这里插入图片描述

    类的公用接口

    C++ 通过类来实现封装性, 把数据和与这些数据有关的操作封装在一个类中. 在声明了一个类以后, 用户主要是通过调用公用的成员函数来实现类提供的功能, 称为消息传递.

    公用成员函数是用户使用类的公用接口 (public interface), 或者说是类的对外接口. 在类外不能直接访问私有数据成员, 但可以通过调用公用成员函数来引用甚至修改私有数据成员.

    在这里插入图片描述

    类的私有实现

    用户不必关系这些功能实现的细节, 而只需要知道调用哪个函数会得到什么结果, 实现什么功能即可. 通过成员函数对数据成员进行操作称为类的实现. 实现的细节对用户是隐蔽的. (信息隐藏)

    类中被操作的数据是私有的, 实现的细节对用户是隐蔽的. 这称为私有实现. (private implementation)
    在这里插入图片描述
    类的公用接口与私有实现的分离形成了信息隐蔽. 将接口与实现分离是软件工程的一个最基本的原则.

    优点:

    1. 如果想扩充累的功能, 只需要修改本类中有关的数据成员和它有关的成员函数. 程序中类外的部分可以不必修改
    2. 如果在编译时发现类中的数据读写有错, 不必检查整个程序. 只需检查本类中访问这些数据的少数成员函数

    方法与消息

    方法 (method) 指类的成员函数在面向对象程序理论中被称为方法, 是指对数据的操作. 一个 “方法” 对应一种操作. 只有被声明为公用的方法才能被外界所激活.

    消息指对公用函数的调用. 外界是通过发 “消息” 来激活有关方法的. 调用对象的成员函数, 就是向对象发出的一个 “消息”. 通知它执行其中的方法.

    面向对象的特点:

    1. 封装性: 各个对象间相对独立
    2. 抽象性: 类是对象的抽象, 对象是类的实例
    3. 继承性: 解决重用问题
    4. 多态性: 由继承产生的相关的不同的类. 其对象对同一消息会做出不同的响应
    展开全文
  • 封装和信息隐藏

    千次阅读 2018-11-08 19:45:24
    文章目录3.1 信息隐藏原则3.1.1 封装与信息隐藏3.1.2 接口扮演的角色3.2 创建对象的基本模式3.2.1 门户大开型对象3.2.2 用命名规范区别私用成员3.2.3 作用域、嵌套函数和闭包3.2.4 用闭包实现私用成员3.3 更多高级...

    为对象创建私用成员是任何面向对象语言中最基本和有用的特性之一。通过将一个方法或属性声明为私用的,可以让对象的实现细节对其他对象保密以降低对象之间的耦合程度,可以保持数据的完整性并对其修改方式加以约束。在代码有许多人参与设计的情况下,这也可以使代码更可靠、更易于调试。简而言之,封装是面向对象的设计的基石。
    尽管JavaScript是一种面向对象的语言,它并不具备用以将成员声明为公用或私用的任何内置机制。目前有几种办法可以用来创建具有公用、私用和特权方法的对象,它们各有优缺点。

    3.1 信息隐藏原则

    3.1.1 封装与信息隐藏

    可以把封装和信息隐藏视为同一个概念的两种表述。信息隐藏是目的,封装是达到这个目的的技术。
    封装可以被定义为对对象的内部数据表现形式和实现细节进行隐藏。要想访问封装过的对象中的数据,只有使用已定义的操作这一种办法。通过封装可以强制实施信息隐藏。许多面向对象语言都使用关键字来说明某些方法和属性应被隐藏。但在JavaScript中没有这样的关键字,我们将使用闭包的概念来创建只允许从对象内部访问的方法和属性。这比使用关键字的办法更复杂。

    3.1.2 接口扮演的角色

    在向其他对象隐藏信息的过程中接口是如何发挥作用的呢?
    接口提供了一份记载着可供公众访问的方法的契约。它定义了两个对象间可以具有的关系。只要接口不变,这个关系的双方都是可替换的。大多数情况下,你将发现对可以使用的方法加以记载会很有好处。不是有了接口就万事大吉,你应该避免公开未定义于接口中的方法。否则其他对象可能会对那些并不属于接口的方法产生依赖,这是不安全的。因为这些方法随时都可能发生改变或被删除,从而导致整个系统失灵。
    一个理想的软件系统应该为所有类定义接口。这些类只向外界提供它们实现的接口中规定的方法,任何别的方法都留作自用。其所有属性都是私有的,外界只能通过接口中定义的存取操作与之打交道。但实际的系统很少能真正达到这样的境界。优质的代码应尽量向这个目标靠拢,但又不能过于刻板,把那些并不需要这些特性的简单项目复杂化。

    3.2 创建对象的基本模式

    JavaScript创建对象的基本模式有3种

    1. 门户大开型,这是最简单的一种,但它只能提供公用成员。
    2. 使用下划线表示私用方法或属性。
    3. 使用闭包来创建真正的私用成员,这些成员只能通过一些特权方法访问。

    以Book为例,该类满足这样的需求:存储关于一本书的数据,并实现一个以HTML形式显示这些数据的方法。
    你只负责创建这个Book类,别人会创建并使用其实例。它会被这样使用:

    // Book (isbn, title, author)
    var theHobbit = new Book('0-395-07122-4', 'The Hobbit', 'J. R. R. Tolkien');
    theHobbit.display();//通过创建HTML element显示数据
    

    3.2.1 门户大开型对象

    用一个函数来做其构造器,它的所有属性和方法都是公开的、可访问的。这些公用属性需要使用this关键字来创建。

    var Book = function(isbn, title, author) {
    	if(isbn == undefined) throw new Error('Book constructor requires an isbn.');
    	this.isbn = isbn;
    	this.title = title || 'No title specified';
    	this.author = author || 'No author specified';
    };
    Book.prototype.display = function () {
    	...
    }
    

    好像提供了ISBN就可以查到书籍了,可是这里有一个最大的问题,你无法检验ISBN数据的完整性,而不完整的ISBN数据有可能导致display方法失灵。如果Book对象在创建的时候没有什么问题,那么在display时也能正常工作才对,但是由于没有进行完整性检查,就不一定了。下面的版本强化了对ISBN的检查。

    var Book = function(isbn, title, author) {
    	if(!this.checkIsbn(isbn)) throw new Error('Book: Invalid ISBN.');
    	this.isbn = isbn;
    	this.title = title || 'No title specified';
    	this.author = author || 'No author specified';
    }
    Book.prototype = {
    	checkIsbn: function(isbn) {
    		if(isbn == undefined || typeof isbn != 'string'){
    			return false;
    		}
    		isbn = isbn.replace(/-/,'');
    		if(isbn.length != 10 && isbn.length !=13) {
    			return false;
    		}
    		var sum = 0;
    		if(isbn.length === 10) {//10位的ISBN
    			if(!isbn.match(/^\d{9}/)) {
    				return false;
    			}
    			for(var i = 0; i < 9; i++) {
    				sum += isbn.charAt(i) * (10-i);
    			}
    			var checksum = sum % 11;
    			if(checksum === 10) checksum = 'X';
    			if(isbn.charAt(9) != checksum) {
    				return false;
    			}
    		}
    		else {//13位的ISBN
    			if(!isbn.match(/^\d{12}/)) {
    				return false;
    			}
    			for(var i = 0; i < 12; i++) {
    				sum += isbn.charAt(i) * ((i % 2 === 0) ? 1 : 3);
    			}
    			var checksum = sum % 10;
    			if(isbn.charAt(12) != checksum) {
    				return false;
    			}		
    		}
    		return true;
    	},
    	display: function() {
    		...
    	}
    };
    

    checkIsbn保证ISBN是一个具有正确的位数和校验和的字符串。这样在创建对象的时候可以对ISBN的有效性进行检查,这可以确保display方法能正常工作。

    但问题又来了,即使在构造的时候能对ISBN进行检验,如果后续其他程序员把其他值赋给isbn,这时就检验不了了,

    theHobbit.isbn = '978-0261103283';
    theHobbit.display();
    

    为了保护内部数据,为每一个属性都提供了取值器和赋值器方法。

    • 取值器方法用于获取属性值,通常以getAttributeName这种形式命名;
    • 赋值器方法用于设置属性值,通常以setAttributeName这种形式命名。

    通过使用赋值器,你可以在把一个新值真正赋给属性之前进行各种检验。

    var Publication = new Interface('Publication', ['getIsbn', 'setIsbn', 'getTitle', 'setTitle', 'getAuthor', 'setAuthor', 'display']);
    
    var Book = function(isbn, title, author) {
    	this.setIsbn(isbn);
    	this.setTitle(title);
    	this.setAuthor(author);
    }
    
    Book.prototype = {
    	checkIsbn: function(isbn) {
    		...
    	},
    	getIsbn: function() {
    		return this.isbn;
    	},
    	setIsbn: function(isbn) {
    		if(!this.checkIsbn()) throw new Error('Book: Invalid ISBN.');
    		this.isbn = isbn;
    	},
    	getTitle: function() {
    		return this.title;
    	},
    	setTitle: function(title) {
    		this.title = title || 'No title specified.';
    	},
    	getAuthor: function() {
    		return this.author;
    	},
    	setAuthor: function(author) {
    		this.author = author || 'No author specified.';
    	},
    	display: function() {
    		...
    	}
    };
    

    这是使用门户大开型对象创建方式所能得到的最好结果。

    这里明确定义了接口、一些对数据具有保护作用的取值器和赋值器方法,以及一些有效性检验方法。

    这里还是有一个漏洞,虽然我们为设置属性提供了赋值器方法,但那些属性仍然是公开的,可以被直接设置的,而在这种方案中却无法阻止这种行为。

    不过这种方法易于使用,创建这样的对象不要求你深入理解作用域或调用链的概念。由于所有方法和属性都是公开的,派生子类和进行单元测试也很容易。唯一的弊端在于无法保护内部数据,而且取值器和赋值器也引入了额外的代码。

    3.2.2 用命名规范区别私用成员

    从本质上来说,这种模式与门户大开型对象创建模式如出一辙,只不过在一些方法和属性的名称前加了下划线以示其私用性而已。这种方法可以解决上一种方法带来的问题:无法阻止其他程序员无意中绕过的所有检验步骤。

    var Book = function(isbn, title, author) {
    	this.setIsbn(isbn);
    	this.setTitle(title);
    	this.setAuthor(author);
    }
    
    Book.prototype = {
    	checkIsbn: function(isbn) {
    		...
    	},
    	getIsbn: function() {
    		return this._isbn;
    	},
    	setIsbn: function(isbn) {
    		if(!this.checkIsbn()) throw new Error('Book: Invalid ISBN.');
    		this._isbn = isbn;
    	},
    	getTitle: function() {
    		return this._title;
    	},
    	setTitle: function(title) {
    		this._title = title || 'No title specified.';
    	},
    	getAuthor: function() {
    		return this._author;
    	},
    	setAuthor: function(author) {
    		this._author = author || 'No author specified.';
    	},
    	display: function() {
    		...
    	}
    };
    

    这种命名规范也可以应用于方法,例如checkIson方法应该是类私有的方法:

    	_checkIsbn: function(isbn) {
    		...
    	},
    

    下划线的这种用法是一个众所周知的命名规范,它表明一个属性或方法仅供对象内部使用,直接访问它或设置它可能会导致意想不到的后果。这有助于防止程序员对它的无意使用,却不能防止对它的有意使用。

    这并不是真正可以用来隐藏对象内部数据的解决方法,它主要适用于非敏感性的内部方法和属性。

    3.2.3 作用域、嵌套函数和闭包

    在讨论真正的私用性方法和属性的实现技术之前,我们先花点时间解释一下这种技术背后的原理。
    在JavaScript中,只有函数具有作用域。也就是说,在一个函数内部声明的变量在函数外部无法访问。私用属性就其本质而言就是希望在对象外部无法访问的变量,所以为实现这种拒访性而求助于作用域这个概念是合乎情理的。

    定义在一个函数中的变量在该函数的内嵌函数中是可以访问的:

    function foo() {
    	var a = 10;
    	function bar() {
    		a *= 2;
    	}
    	bar();
    	return a;
    }
    foo();//20
    

    一个简单的闭包例子:

    function foo() {
    	var a = 10;
    	function bar() {
    		a *= 2;
    		return a;
    	}
    	return bar;
    }
    var baz = foo();
    baz(); // 20
    baz(); // 40
    baz(); // 80
    

    可以看到,函数是在foo外部被调用,但它依然能够访问a,这是因为JavaScript中的作用域是词法性的。函数是运行在定义它们的作用域中,而不是运行在调用它们的作用域中。所以,bar被定义在foo的作用域中,就能访问foo中定义的变量,即使foo的执行已经结束。

    3.2.4 用闭包实现私用成员

    var Book = function (newIsbn, newTitle, newAuthor) {
    	// Private attributes.
    	var isbn, title, author;
    
    	// Private method.
    	function checkIsbn(isbn) {
    		...
    	}
    
    	// Privileged methods.
    	this.getIsbn = function () {
    		return isbn;
    	};
    	this.setIsbn = function (newIsbn) {
    		if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.');
    		isbn = newIsbn;
    	};
    
    	this.getTitle = function () {
    		return title;
    	};
    	this.setTitle = function (newTitle) {
    		title = newTitle || 'No title specified';
    	};
    
    	this.getAuthor = function () {
    		return author;
    	};
    	this.setAuthor = function (newAuthor) {
    		author = newAuthor || 'No author specified';
    	};
    
    	// Constructor code.
    	this.setIsbn(newIsbn);
    	this.setTitle(newTitle);
    	this.setAuthor(newAuthor);
    };
    
    // Public, non-privileged methods.
    Book.prototype = {
    	display: function() {
    		...
    	}
    }
    

    代码解读:

    • 使用var声明的变量或方法是私有的,因为没有用this关键字,这意味着它们只存在于Book构造器中
    • 要访问这些私有的变量和方法,需在Book中使用this关键字声明方法,这些方法被称为特权方法(privileged method),它们是公用的方法,因为声明在Book内部,所以在对象外部可以访问到私用属性和方法。
    • 任何不需要直接访问私用属性的方法都可以在Book.prototype中声明。
    • 每生成一个新的对象实例都将为每一个私用方法和特权方法生成一个新的副本。这会比其他做法更耗内存。
    • 不利于派生子类,因为所派生出的子类不能访问超类的任何私用属性或方法。相比之下,在大多数语言中,子类都能访问超类的所有私用属性和方法。故在JavaScript中用闭包实现私用成员导致的派生问题被称为“继承破坏封装”

    3.3 更多高级对象创建模式

    前面学习了创建对象的3种基本模式,下面再对一些高级一点的模式做一个简介。

    3.3.1 静态方法和属性

    静态成员是在类的层次上操作的,而不是在实例的层次上操作。每个静态成员都只有一份。
    下面是添加了静态属性和方法的Book类:

    var Book = (function() {
    	// Private static attributes.
    	var numOfBooks = 0;
    
    	// Private static method.
    	function checkIsbn(isbn) {
    		...
    	}
    
    	// Return the constructor.
    	return function (newIsbn, newTitle, newAuthor) {
    		// Private attributes.
    		var isbn, title, author;
    	
    		// Private method.
    		function checkIsbn(isbn) {
    			...
    		}
    	
    		// Privileged methods.
    		this.getIsbn = function () {
    			return isbn;
    		};
    		this.setIsbn = function (newIsbn) {
    			if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.');
    			isbn = newIsbn;
    		};
    	
    		this.getTitle = function () {
    			return title;
    		};
    		this.setTitle = function (newTitle) {
    			title = newTitle || 'No title specified';
    		};
    	
    		this.getAuthor = function () {
    			return author;
    		};
    		this.setAuthor = function (newAuthor) {
    			author = newAuthor || 'No author specified';
    		};
    	
    		// Constructor code.
    		numOfBooks++;
    		if(numOfBooks > 50) throw new Error('Book: Only 50 instances of Book can be created.');
    		this.setIsbn(newIsbn);
    		this.setTitle(newTitle);
    		this.setAuthor(newAuthor);	
    	}
    })();
    
    // 创建Public static method.
    Book.convertToTitleCase = function(inputString) {
    	...
    };
    
    // Public, non-privileged methods.
    Book.prototype = {
    	display: function() {
    		...
    	}
    }
    

    这里创建了一个闭包,将构造器从原来的普通函数变成了一个内嵌函数,并且作为返回值赋给变量Book。外层的函数只是用于创建一个可以用来存放静态私用成员的闭包,这些静态私用成员不会为Book的每一个实例都创建一个新的副本。
    如何判断一个私用方法是否应该被设计为静态方法?
    看它是否需要访问任何实例数据。如果它不需要,则设计为静态方法会更有效率,因为它只会被创建一份。

    3.3.2 常量

    常量是一些不能被修改的变量。在JavaScript中,可以通过创建只有取值器而没有赋值器的私有变量来模仿常量。

    var Class = (function() {
    	// Constants (created as private static attributes.).
    	var UPPER_BOUND	= 100;
    	// Constructor.
    	var ctor = function (constructorArgument) {
    		...
    	};
    	// Privileged static method.
    	ctor.getUPPER_BOUND = function() {
    		return UPPER_BOUND;
    	};
    	...
    	// Return the constructor
    	return ctor;
    })();
    

    如果需要使用许多常量,但你不想为每个常量都创建一个取值器方法,那么可以创建一个通用的取值器方法,如下:

    var Class = (function() {
    	// Constants (created as private static attributes.).
    	var constants ={
    		UPPER_BOUND: 100,
    		LOWER_BOUND: -100
    	};
    	// Constructor.
    	var ctor = function (constructorArgument) {
    		...
    	};
    	// Privileged static method.
    	ctor.getConstant = function(name) {
    		return constants[name];
    	};
    	...
    	// Return the constructor
    	return ctor;
    })();
    Class.getConstant('UPPER_BOUND');
    

    3.3.3 单体和对象工厂

    单体模式和工厂模式也使用闭包来创建受保护的变量空间,后面部分会详细讨论这两种模式。在此简单介绍一下:

    • 单体模式使用一个由外层函数返回的对象字面量来公开特权成员,而私用成员则被保护性地封装在外层函数的作用域中。它使用的技术是:外层函数在定义之后立即执行,其结果被赋给一个变量。本章的例子中外层函数返回的都是一个函数,而单体模式返回的则是一个对象字面量。
    • 对象工厂也可以使用闭包来创建具有私用成员的对象。其最简形式就是一个类构造。

    3.4 封装之利

    要是在创建对象时不用操心闭包和特权方法,事情就会简单得多。那么,不厌其烦的隐藏实现细节究竟有什么好处?
    封装保护了内部数据的完整性。通过将数据的访问途径设置为取值器和赋值器这两个方法,可以获得对取值和赋值的完全控制。这可以减少其他函数所需的错误检查代码的数量,并确保数据不会处于无效状态。
    封装提高了对象的可重用性,使其在必要的时候可以被替换。使用私用变量也有助于避免命名空间冲突。
    封装还使你可以大幅改动对象的内部细节,而不会影响到其他部分的代码。总的来说,代码的修改变得更轻松,如果对象的内部数据都是公开的话,你不可能完全清楚代码的修改会带来什么结果。

    3.5 封装之弊

    私用方法很难进行单元测试。因为他们及其内部变量都是私用的,在对象外部无法访问到它们。要么通过使用公用方法来提供访问途径(这样一来就葬送了使用私用方法所带来的大多数好处),要么设法在对象内部定义并执行所有测试单元。最好的解决办法是只对公用方法进行单元测试。这是一种广为接受的处理方式。
    使用封装意味着不得不与复杂的作用域链打交道,而这会使错误调试变得更加困难。有时候会很难区分来自不同作用域的大批同名变量。这个问题不是经过封装的对象所特有的,但实现私用方法和属性所需的闭包会让它变得更复杂。
    过度封装也是一个潜在的问题。
    最大的问题在于JavaScript中实现封装的困难。JavaScript本来就是一门与多数面向对象语言大相径庭的语言,而封装技术设计的调用链和定义后立即执行的匿名函数等概念更是加大了学习难度。此外,封装技术的应用还使不熟悉特定模式的人难以理解既有代码。注释和程序文档可以提供一些帮助,但并不能完全解决这个问题。

    3.6 小结

    本章讨论了信息隐藏的概念以及如何用封装这种手段来实现它。因为JavaScript没有对封装提供内置的支持,所以其实现必须依赖于一些其他技术。本书后面的多数章节都依赖于这些基本技术,因此你得好好品味一下本节的内容。只要理解了JavaScript中作用域的特点,你就能模仿出各种面向对象的技术。

    展开全文
  • C++隐藏概念(例子解析)

    千次阅读 热门讨论 2018-06-01 18:33:09
    所谓的隐藏,指的是派生类类型的对象、指针、引用访问基类和派生类都有的同名函数时,访问的是派生类的函数,即隐藏了基类的同名函数。隐藏规则的底层原因其实是C++的名字解析过程。在继承机制下,派生类的类域被...
  • 信息隐藏技术综述

    千次阅读 2007-03-01 19:58:00
    信息隐藏技术综述蔡洪亮 李波 卢星 杨明摘要:介绍了信息隐藏技术的概念、模型及分类;给出了信息隐藏技术的五大特征;分析了两种典型的信息隐藏算法:时域替换技术和变换域技术;描述了信息隐藏在现实中主要的应用领域;...
  • 好的类接口就像是冰山的... 1972年,David Parnas 发表了一篇题为《论将系统分解为模块的准则》的论文首次让公众注意到信息隐藏这一概念信息隐藏式结构程序设计与面向对象设计的基础之一。在面向对象设计中,它又
  • 1.1 信息安全概念 欢迎加入最棒的信息安全工程师社群,分享信息安全工程师备考干货资料。 备考交流QQ群:39460595 https://www.moondream.cn/?p=517 一.大纲要求 1.1 了解网络空间的概念、网络空间安全学科的...
  • 信息隐藏(Information Hiding)原则

    千次阅读 2011-07-26 16:25:44
    信息隐藏(Information Hiding)原则黄国强 2006-10-25 信息隐藏是结构化设计与面向...在结构化中函数的概念和面向对象的封装思想都来源于信息隐藏。软件业对这个原则的认同也是最近十年的事情。 David Parnas在1972
  • C++编程规范之11:隐藏信息

    千次阅读 2013-12-08 21:41:57
    摘要:  不要泄密,不要公开提供抽象的实体的内部信息。  为了尽量减少操作抽象的调用代码和抽象的实现之间的依赖性... 信息隐藏主要从一下两方面降低了项目的成为,加快了项目的进度,减少了项目的风险。 1. 
  • 隐写术概念:将秘密信息嵌入到看上去普通的信息中进行传 送,以防止第三方检测出秘密信息 隐写系统的要求: 不可察觉性 盲隐藏 安全性主要取决于第三方区分载体 对象和隐写对象之间差别的能力 载体对象要含有...
  • 有关封装和信息隐藏的误区

    万次阅读 热门讨论 2010-08-29 00:10:00
    上次介绍Python的面对对象特性,其中扯到了封装(Encapsulation)等概念。当时为了不跑题,没有深入聊这些概念。考虑到很多开发人员对这些概念,经常混淆不清。今天再专门来说一下。
  • 对于企业的领导人来说,理解安全和隐私的概念非常重要,这可以帮助他们在信息保护和隐私管理方面做出正确的决策。  那么, 安全和隐私的区别究竟在哪里呢?下面的十条概念能够帮助大家更好的理解二者的不同: 1....
  • 设计的核心任务之二:信息隐藏

    千次阅读 2012-04-17 23:59:45
    局部变量把信息隐藏在方法之内,成员或属性把信息隐藏在类之内,而全局变量等于不做信息隐藏信息隐藏和前面提到的层次问题并不能分割开来。从基本趋势来看,信息隐藏做的越彻底,层次也必然就会越多。而前面曾经...
  • 封装的概念

    千次阅读 2019-03-10 17:53:08
    封装也称为信息隐藏,是利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。...
  • MySQL 8.0 新特性之隐藏字段

    千次阅读 2021-02-17 21:41:47
    MySQL 8.0.23 版本增加了一个新的功能:隐藏字段...本文给大家介绍一下 MySQL 隐藏字段的相关概念和具体实现,包括如何创建隐藏字段和查询隐藏字段、如何查看隐藏字段相关的字典信息以及二进制日志对隐藏字段的处理。
  • 面向对象基本概念

    万次阅读 多人点赞 2019-02-06 21:56:15
    面向对象就是:把数据及对数据的操作方法放在一起,作为一个相互依存的整体...对象即为人对各种具体物体抽象后的一个概念,人们每天都要接触各种各样的对象,如手机就是一个对象。 面向对象编程(OOP:object-orie...
  • 从这个角度来说,未来阿里云智能事业群会更加突出“智能”概念,而淡化“云”的概念。这也将推动AI被社会普遍所接受。   比如阿里云这两年推出的系列ET大脑概念,已经覆盖了城市、工业、零售、金融、汽车、家庭等多...
  • C++ 覆盖 重载 隐藏 浅析

    千次阅读 2015-07-01 09:17:47
    然后,介绍多态与虚函数等相关概念,并给出代码示例,以比较说明基类指向子类对象地址的指针与子类指向强制转换为子类的基类对象地址的指针在虚函数与一般成员函数(也是隐藏机制的函数)上,输出情况的异同;...
  • DDD的基本概念介绍

    千次阅读 2020-01-10 15:06:17
    DDD的基本概念澄清 目录 什么是DDD Ubiquitous language (通用语言) Layered architecture(分层架构) Entity(实体) Value Ojbect(值对象) Service(服务) Module(模块) Aggregate(聚合) ...
  • URL的概念

    千次阅读 多人点赞 2019-06-02 10:04:13
    2、隐藏身份 3、实现负载均衡 Get和Post请求模式的区别 Http协议定义了很多与服务器交互的方法,最基本的4种,分别是get,post,put,delete,一个URL地址用于描述一个网络中的资源,而HTTP中的get,post,put,delete...
  • 网页游戏开发概念

    万次阅读 2019-04-01 16:10:48
    to-Peer 对等网络) 架构2.1.2 轮询(不建议使用的链接技术)2.1.3 Socket服务器3 安全3.1 逻辑安全3.2 物理安全3.2.1 问题与解决方案3.2.2 防火墙3.2.3 常见黑客行为6 游戏逻辑决策位置6.1 概念6.1.1 客户端权威型...
  • ES基础概念

    千次阅读 2017-08-14 15:47:26
    刚开始学习ES,就脚踏实地从基础概念入手学习,以下是ES中常见概念及解释: 1.概述 Elasticsearch是 面向文档(document oriented) 的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,...
  • 机器视觉的一些概念

    万次阅读 2017-05-23 14:38:14
    一些关于机器视觉的概念 2013-07-27 12:19 20722人阅读 评论(2) 收藏 举报 本文章已收录于: 目录(?)[+] 物方远心镜头及像方远心镜头介绍 四种工业相机接口技术的比较 视觉...
  • AI基本概念和应用

    万次阅读 2017-11-09 22:13:58
    从海量数据中“挖掘”隐藏信息,从大量的、模糊的、随机的实际应用数据中,提炼隐含的、规律性的,并且又能被理解的信息。数据挖掘基于数据仓库。 DL:Deep Learning,深度学习。 机器学习的一个子集,用...
  • 深度学习入门必须理解这25个概念

    万次阅读 多人点赞 2017-05-22 21:11:51
    想象一下,当我们得到新信息时我们该怎么做。当我们获取信息时,我们一般会处理它,然后生成一个输出。类似地,在神经网络的情况下,神经元接收输入,处理它并产生输出,而这个输出被发送到其他神经元用于进一步处理...
  • AI:人工智能概念之《Google发布机器学习术语表 (中英对照)》——持续更新ML、DL相关概念2018年4月! 相关文章AI:人工智能概念之《Google发布机器学习术语表 (中英对照)》——持续更新ML、DL相关概念2018年4月...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 180,925
精华内容 72,370
关键字:

信息隐藏的概念