精华内容
下载资源
问答
  • 在数字图像处理过程中,常见的几种色彩模式有RGB, HSL\HSV和YCbCr RGB: 通过对红(R), 绿(G), 蓝(B)三个颜色通道的变化和叠加来得到其它颜色,三个分量的范围...
    在数字图像处理过程中,常见的几种色彩模式有RGB, HSL\HSV和YCbCr
    
    
    RGB: 通过对红(R), 绿(G), 蓝(B)三个颜色通道的变化和叠加来得到其它颜色,三个分量的范围都是[0, 255]
    HSL\HSV: 将RGB色彩模式中的点在圆柱坐标系中进行表述,分为色相(Hue), 饱和度(Saturation), 亮度(Lightness)\明度(Value)三个通道。
    
    • 色相(H):色彩的基本属性,就是日常所说的颜色名称,如红色、黄色等,取值范围为[0, 360);

    • 饱和度(S):色彩的纯度,越高色彩越纯,低则逐渐变灰,取值范围[0, 100%];

    • 明度(V),亮度(L):像素灰度值的强度,亮度越高则图像越发白,否则图像越黑,取值范围[0, 100%];

    YCbCr: 一般我们所说的YUV都是指YCbCr,YCbCr 有许多取样格式,如 444,422,420等

    • Y:明亮度,像素灰度值的强度;

    • Cb:蓝色色度分量;

    • Cr:红色色度分量;

    Cb和Cr代表的是色度,描述影像色彩和饱和度,用于指定像素的颜色

    在数字图像处理中,选择合适的色彩模式往往能达到事半功倍的效果

    此处以Android平台上操作图像的亮度,对比度和饱和度来进行说明,首先了解下三者的概念:

    • 亮度:像素灰度值的强度,亮度越高则图像越发白,否则图像越黑;

    • 饱和度:色彩的纯度,越高色彩越纯越亮,低则逐渐变灰变暗;

    • 对比度:图像中像素之间的差异,对比度越高图像细节越突出,反之细节不明显;

    从上面的概念上来看,如果要操作图像的亮度和饱和度,在HSL\HSV色彩空间中进行是最方便的,直接操作相应的分量即可;而对比度的操作可以直接在RGB色彩空间中进行

    在Android中,我们用ImageView显示一张图片

    然后拿到ImageView内部的bitmap对象

    (imageView.drawable as BitmapDrawable).bitmap
    
    从bitmap中获取RGB数据
    fun fetchRgbaFromBitmap(bitmap: Bitmap): IntArray {    val buffer = ByteBuffer.allocate(bitmap.byteCount).order(ByteOrder.nativeOrder())    bitmap.copyPixelsToBuffer(buffer)    val rgbaBytes = buffer.array()    val rgba = IntArray(rgbaBytes.size)    val count = rgbaBytes.size / 4    for (i in 0 until count) {        rgba[i * 4] = rgbaBytes[i * 4].toInt() and 0xff           // R        rgba[i * 4 + 1] = rgbaBytes[i * 4 + 1].toInt() and 0xff   // G        rgba[i * 4 + 2] = rgbaBytes[i * 4 + 2].toInt() and 0xff   // B        rgba[i * 4 + 3] = rgbaBytes[i * 4 + 3].toInt() and 0xff   // A    }    return rgba}
    RGB色彩空间中调整对比度算法(调整对比度的算法很多,此处只是一个简单实现):
    R,G,B分量除255做归一化处理;
    ((归一化的分量 -  0.5)  *  饱和度系数  +  0.5)  *  255;
    核心代码:
     val count = originBitmapRgba!!.size / 4 for (i in 0 until count) {      var r = originBitmapRgba!![i * 4]      var g = originBitmapRgba!![i * 4 + 1]      var b = originBitmapRgba!![i * 4 + 2]      val a = originBitmapRgba!![i * 4 + 3]
          val cr = ((r / 255f) - 0.5f) * CONTRACT_RATIO      val cg = ((g / 255f) - 0.5f) * CONTRACT_RATIO      val cb = ((b / 255f) - 0.5f) * CONTRACT_RATIO
          r = ((cr + 0.5f) * 255f).toInt()      g = ((cg + 0.5f) * 255f).toInt()      b = ((cb + 0.5f) * 255f).toInt()      val newColor = Color.rgb(Util.clamp(r, 0, 255),                Util.clamp(g, 0, 255),  Util.clamp(b, 0, 255))      ColorUtils.setAlphaComponent(newColor, a)      tmpBitmapPixels!![i] = newColor}
    对比度系数CONTRACT_RATIO为1.5的效果
    
    对比度系数CONTRACT_RATIO为3的效果,可以看到图像细节更突出,画面更有层次感
    
    亮度和饱和度的调节也可以在RGB色彩空间中应用相关算法进行,此处先将RGB转化到HSL色彩空间,然后根据系数对S分量和L分量进行增益即可
    从RGB到HSL\HSV的转换算法如下(摘自百度百科):
    
    从HSL\HSV到RGB的转化算法,详情查看百度百科...
    
    Android中RGB和HSL的相互转化,SDK已经帮我们实现好了, 
    ColorUtils#RGBToHSL:
    
    /**
    * Convert RGB components to HSL (hue-saturation-lightness).
    * <ul>
    * <li>outHsl[0] is Hue [0 .. 360)</li>
    * <li>outHsl[1] is Saturation [0...1]</li>
    * <li>outHsl[2] is Lightness [0...1]</li>
    * </ul>
    *
    * @param r      red component value [0..255]
    * @param g      green component value [0..255]
    * @param b      blue component value [0..255]
    * @param outHsl 3-element array which holds the resulting HSL components
    */
    public static void RGBToHSL(@IntRange(from = 0x0, to = 0xFF) int r,
                @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
                @NonNull float[] outHsl)
    
    ColorUtils#HSLToColor:
    
    /**
    * Convert HSL (hue-saturation-lightness) components to a RGB color.
    * <ul>
    * <li>hsl[0] is Hue [0 .. 360)</li>
    * <li>hsl[1] is Saturation [0...1]</li>
    * <li>hsl[2] is Lightness [0...1]</li>
    * </ul>
    * If hsv values are out of range, they are pinned.
    * @param hsl 3-element array which holds the input HSL components
    * @return the resulting RGB color
    */
    @ColorInt
    public static int HSLToColor(@NonNull float[] hsl)
    
    调整饱和度核心代码:
    
    val count = originBitmapRgba!!.size / 4
    for (i in 0 until count) {
       val r = originBitmapRgba!![i * 4]
       val g = originBitmapRgba!![i * 4 + 1]
       val b = originBitmapRgba!![i * 4 + 2]
       val a = originBitmapRgba!![i * 4 + 3]
       ColorUtils.RGBToHSL(r, g, b, hsl)
       val s = hsl[1]  * SATURATION_RATIO
       hsl[1] = Util.clamp(s,0f, 1f)
       val newColor = ColorUtils.HSLToColor(hsl)
       ColorUtils.setAlphaComponent(newColor, a)
       tmpBitmapPixels!![i] = newColor
    }
    
    饱和度系数SATURATION_RATIO为1.5的效果, 可以看到图像变亮变纯
    
    饱和度系数SATURATION_RATIO为0.5的效果,可以看到图像变暗变灰
    
    调整亮度核心代码:
    
    val count = originBitmapRgba!!.size / 4
    for (i in 0 until count) {
       val r = originBitmapRgba!![i * 4]
       val g = originBitmapRgba!![i * 4 + 1]
       val b = originBitmapRgba!![i * 4 + 2]
       val a = originBitmapRgba!![i * 4 + 3]
       ColorUtils.RGBToHSL(r, g, b, hsl)
       hsl[2] = hsl[2] * LIGHTNESS_RATIO
       val newColor = ColorUtils.HSLToColor(hsl)
       ColorUtils.setAlphaComponent(newColor, a)
       tmpBitmapPixels!![i] = newColor
    }
    
    亮度系数LIGHTNESS_RATIO为1.5的效果, 可以看到图像整体发白
    
    亮度系数LIGHTNESS_RATIO为0.5的效果,可以看到图像整体变黑了
    

    DEMO

    代码传送门:

    https://github.com/sifutang/ImageProcess.git 


    技术交流,欢迎加我微信:ezglumes ,拉你入技术交流群。

    推荐阅读:

    音视频面试基础题

    OpenGL ES 学习资源分享

    一文读懂 YUV 的采样与格式

    OpenGL 之 GPUImage 源码分析

    推荐几个堪称教科书级别的 Android 音视频入门项目

    觉得不错,点个在看呗~

    展开全文
  • RGB是色光的色彩模式。R代表红色,G代表绿色,B代表蓝色,三色彩叠加形成了其它的色彩。因为三种颜色都有256个亮度水平级,所以三色彩叠加就形成1670万种颜色了。也就是真彩色,通过它们足以在现绚丽的世界。在...

    RGB模式

    RGB是色光的色彩模式。R代表红色,G代表绿色,B代表蓝色,三种色彩叠加形成了其它的色彩。因为三种颜色都有256个亮度水平级,所以三种色彩叠加就形成1670万种颜色了。也就是真彩色,通过它们足以在现绚丽的世界。在RGB模式中,由红、绿、蓝相叠加可以产生其它颜色,因此该模式也叫加色模式。所有显示器、投影设备以及电视机等等许多设备都依赖于这种加色模式来实现的。就编辑图象而言,RGB色彩模式也是最佳的色彩模式,因为它可以提供全屏幕的24bit的色彩范围,即真彩色显示。但是,如果将RGB模式用于打印就不是最佳的了,因为RGB模式所提供的有些色彩已经超出了打印的范围之外,因此在打印一幅真彩色的图象时,就必然会损失一部分亮度,并且比较鲜艳的色彩肯定会失真的。。这主要因为打印所用的是CMYK模式,而CMYK模式所定义的色彩要比RGB模式定义的色彩少很多,因此打印时,系统自动将RGB模式转换为CMYK模式,这样就难免损失一部分颜色,出现打印后失真的现象。

    CMYK模式

    当阳光照射到一个物体上时,这个物体将吸收一部分光线,并将剩下的光线进行反射,反射的光线就是我们所看见的物体颜色。这是一种减色色彩模式,同时也是与RGB模式的根本不同之处。不但我们看物体的颜色时用到了这种减色模式,而且在纸上印刷时应用的也是这种减色模式。按照这种减色模式,就衍变出了适合印刷的CMYK色彩模式。CMYK代表印刷上用的四种颜色,C代表青色,M代表洋红色,Y代表黄色,K代表黑色。因为在实际引用中,青色、洋红色和黄色很难叠加形成真正的黑色,最多不过是褐色而已。因此才引入了K——黑色。黑色的作用是强化暗调,加深暗部色彩。CMYK模式是最佳的打印模式,RGB模式尽管色彩多,但不能完全打印出来。那么是不是在编辑的时候就采用CMYK模式呢?不是,原因如下:用CMYK模式编辑虽然能够避免色彩的损失,但运算速度很慢。主要因为:1、即使在CMYK模式下工作,Photoshop也必须将CMYK模式转变为显示器所使用的RGB模式。2、对于同样的图象,RGB模式只需要处理三个通道即可,而CMYK模式则需要处理四个 馈?/p>由于用户所使用的扫描仪和显示器都是RGB设备,所以无论什么时候使用CMYK模式工作都有把RGB模式转换为CMYK模式这样一个过程。因此,是否应用CMYK模式进行编辑都成在RGB模式和CMYK模式转换的问题。这里给个建议,也算是我的一点经验吧。先用RGB模式进行编辑工作,再用CMYK模式进行打印工作,在打印前才进行转换,然后加入必要的色彩校正,锐化和休整。这样虽然使Photoshop在CMYK模式下速度慢一些,但可节省大部分编辑时间。为了快速预览CMYK模式下图象的显示效果,而不转换模式可以使用View菜单下的CMYK Preview(CMYK 预览)命令。这种打印前的模式转换,并不是避免图象损失最佳的途径,最佳方法是将Lab模式和CMYK模式相结合使用,这样可以最大程度的减少图象失真。

     LAB模式

    Lab模式是有国际照明委员会(CIE)于1976年(哇,好遥远呀。)公布的一种色彩模式。你已经明白了:RGB模式是一种发光屏幕的加色模式,CMYK模式是一种颜色反光的印刷减色模式。那么,Lab有是什么处理模式呢?Lab模式既不依赖光线,也不依赖于颜料,它是CIE组织确定的一个理论上包括了人眼可以看见的所有色彩的色彩模式。Lab模式弥补了RGB和CMYK两种色彩模式的不足。Lab模式由三个通道组成,但不是R、G、B通道。它的一个通道是亮度,即L。另外两个是色彩通道,用A和B来表示。A通道包括的颜色是从深绿色(底亮度值)到灰色(中亮度值)再到亮粉红色(高亮度值);B通道则是从亮蓝色(底亮度值)到灰色(中亮度值)再到黄色(高亮度值)。因此,这种色彩混合后将产生明亮的色彩。Lab模式所定义的色彩最多,且与光线及设备无关并且处理速度与RGB模式同样快,比CMYK模式快很多。因此,可以放心大胆的在图象编辑中使用Lab模式。而且,Lab模式在转换成CMYK模式时色彩没有丢失或被替换。因此,最佳避免色彩损失的方法是:应用Lab模式编辑图象,再转换为CMYK模式打印输出。当你将RGB模式转换成CMYK模式时,Photoshop将自动将RGB模式转换为Lab模式,再转换为CMYK模式。在表达色彩范围上,处于第一位的是Lab模式,第二位的是RGB模式,第三位是CMYK模式。

    HSB模式

    在介绍完三种主要的色彩模式后,现在介绍另一种色彩模式——HSB色彩模式,它在色彩汲取窗口中才会出现。在HSB模式中,H表示色相,S表示饱和度,B表示亮度。色相:是纯色,即组成可见光谱的单色。红色在0度,绿色在120度,蓝色在240度。它基本上是RGB模式全色度的饼状图。饱和度:表示色彩的纯度,为0时为会色。白、黑和其他灰色色彩都没有饱和度的。在最大饱和度时,每一色相具有最纯的色光。亮度:是色彩的明亮读。为0时即为黑色。最大亮度是色彩最鲜明的状态专色通道专色通道,可以保存专色信息的通道——即可以作为一个专色版应用到图像和印刷当中,这是它区别于Alpha通道的明显之处。同时,专色通道具有Alpha通道的一切特点:保存选区信息、透明度信息。每个专色通道只是一个以灰度图形式存储相应专色信息,与其在屏幕上的彩色显示无关。专色及其特点专色油墨是指一种预先混合好的特定彩色油墨(或叫特殊的预混油墨),用来替代或补充印刷色(CMYK)油墨,如明亮的橙色、绿色、荧光色、金属金银色油墨等,或者可以是烫金版、凹凸版等,还可以作为局部光油版等等,它不是靠CMYK四色混合出来的,每种专色在付印时要求专用的印版 (可以简单理解为一付专色胶片、印刷时为专色单独晒版),专色意味着准确的颜色。专色有以下几个特点:1.准确性。每一种专色都有其本身固定的色相,所以它解决了印刷中颜色传递准确性的问题;2.实地性。专色一般用实地色定义颜色,而无论这种颜色有多浅。当然,也可以给专色加网,以呈现专色的任意深浅色调;3.不透明性和透明性。蜡笔色(含有不透明的白色)、黑色阴影(含有黑色)和金属色是相对不透明的,纯色和漆色是相对透明的;4.表现色域宽。专色色域很宽,超过了 RGB、CMYK的表现色域,所以,大部分颜色是用CMYK四色印刷油墨无法呈现的。我们在使用专色和专色通道之前,要清楚专色图像是否需要镂空;专色油墨叠印在其它油墨上时,如何防止龟纹的出现;如果要把专色图像导入排版软件或矢量软件,又如何把专色图像文件保存为DCS 2.0 EPS文件等问题都需要清楚。

    展开全文
  • 5、几种常见的设计模式 5.1、工厂模式 5.2、抽象工厂模式 5.3、单例模式 5.4、适配器模式 5.5、装饰器模式 5.6、代理模式 5.7、策略模式 5.8、观察者模式 1、什么是设计模式?设计模式有什么用...

    目录

    1、什么是设计模式?设计模式有什么用?

    2、设计模式应该遵循的面向对象设计原则

    3、设计模式的六大原则

    4、设计模式的四种类型(包括J2EE设计模式)

    5、几种常见的设计模式

    5.1、工厂模式

    5.2、抽象工厂模式

    5.3、单例模式

    5.4、适配器模式

    5.5、装饰器模式

    5.6、代理模式

    5.7、策略模式

    5.8、观察者模式


    1、什么是设计模式?设计模式有什么用?

    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案。

    2、设计模式应该遵循的面向对象设计原则

    1994年,在由设计模式四人帮GOF出版的著作Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素)中提出,设计模式应该遵循以下两条面向对象设计原则:

    • 对接口编程而不是对实现编程
    • 优先使用对象组合而不是继承

    “对接口编程而不是对实现编程”,对于这句话我的理解就是:要善于使用多态。变量的声明尽量使用超类型(父类),而不是某个具体的子类,超类型中的各个具体方法的实现都是写在不同的子类中。程序在执行时能够根据不同的情况来调用到不同的子类方法,这样做更加灵活,并且我们在声明一个变量时无需关心以后执行时的真正的数据类型是哪种(某个子类类型),这是种解耦合(松耦合)的思想。实例代码如下:

    package Test;
    
    public interface Animal {
        public void makenoise();
    }
    
    
    package Test;
    
    public class Dog implements Animal {
        @Override
        public void makenoise() {
            System.out.println("汪汪汪!");
        }
    }
    
    class Cat implements Animal{
        @Override
        public void makenoise() {
            System.out.println("喵喵喵!");
    
    
    package Test;
    
    public class AnimalTest {
        public static void hearnoise(Animal animal){
            animal.makenoise();
        }
        public static void main(String[] args) {
            AnimalTest.hearnoise(new Dog());
            AnimalTest.hearnoise(new Cat());
        }
    }
    
    
    执行结果:
    汪汪汪!
    喵喵喵!

    3、设计模式的六大原则

    1. 开闭原则(Open Close Prinprinciple),开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。
    2. 里氏代换原则(Liskov Substitution Principle),里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范(LSP我曾经在另一篇文章重新思考接口和抽象类中举了一个例子)。
    3. 依赖倒转原则(Dependence Inversion Principle),这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
    4. 接口隔离原则(Interface Segregation Principle),使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
    5. 迪米特法则,又称最少知道原则(Demeter Principle),一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
    6. 合成复用原则(Composite Reuse Principle),尽量使用合成/聚合的方式,而不是使用继承。

    4、设计模式的四种类型(包括J2EE设计模式)

    创建型模式,这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。创建者模式包括以下几种设计模式:

    • 工厂模式
    • 抽象工厂模式
    • 单例模式
    • 建造者模式
    • 原型模式

    结构型模式,这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。结果型模式包括以下几种设计模式:

    • 适配器模式
    • 桥接模式
    • 过滤器模式
    • 组合模式
    • 装饰器模式
    • 外观模式
    • 享元模式
    • 代理模式

    行为型模式,这些设计模式特别关注对象之间的通信。行为型模式包括以下几种设计模式:

    • 责任链模式
    • 命令模式
    • 解释器模式
    • 迭代器模式
    • 中介者模式
    • 备忘录模式
    • 观察者模式
    • 状态模式
    • 空对象模式
    • 策略模式
    • 模板模式
    • 访问者模式

    J2EE模式,这些设计模式特别关注表现层,这些模式是由Sun Java Center鉴定的。J2EE模式包括以下几种设计模式:

    • MVC模式
    • 业务代表模式
    • 组合实体模式
    • 数据访问对象模式
    • 前端控制器模式
    • 拦截过滤器模式
    • 服务器定位器模式
    • 传输对象模式

    5、几种常见的设计模式

    5.1、工厂模式

    工厂模式(Factory Pattern)是Java中最常见的设计模式之一,属于创建者模式。顾名思义,它的思路是设计一个对象生产工厂,它提供了一种绝佳的创建对象的方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过一个共同的接口来创建对象。

    主要解决的问题:解决接口选择的问题。定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

    优点

    1. 一个调用者想创建一个实例对象,只需要知道其名字就行。
    2. 扩展性高,如果想增加一个产品,只要扩展工厂类就行。
    3. 屏蔽了产品的具体实现,调用者只关心产品的接口。

    缺点

    1. 每次增加一个产品,都需要增加一个具体实现类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂性,同时也增加了系统具体类的依赖,这并不是什么好事。

    实现示例:就以一家生产多个不同品牌的汽车生产厂为例,先创建一个Car接口和三个实现类BENZ、BMW、TOYOTA,再定义一个工厂类CarFactory。我们使用这个工厂类CarFactory来生产不同品牌的汽车。

    package FactoryDemo;
    
    public interface Car {
        public void Brand();
    }
    
    public class BENZ implements Car{
        @Override
        public void Brand() {
            System.out.println("生产一辆奔驰");
        }
    }
    
    public class BMW implements Car{
        @Override
        public void Brand() {
            System.out.println("生产一辆宝马");
        }
    }
    
    public class TOYOTA implements Car{
        @Override
        public void Brand() {
            System.out.println("生产一辆丰田");
        }
    }
    
    //用来生成汽车的工厂类
    //equalsIgnoreCase()方法只能比较字符串,equals()可以比较字符串和对象,且equalsIgnoreCase()
    //中不区别大小写,A-Z和a-z是一样的
    public class CarFactory {
        public Car getcar(String carbrand) {
            if (carbrand.equalsIgnoreCase("BENZ")) {
                return new BENZ();
            } else if (carbrand.equalsIgnoreCase("BMW")) {
                return new BMW();
            } else if (carbrand.equalsIgnoreCase("TOYOTA")) {
                return new TOYOTA();
            } else
                System.out.println("对不起我们不生产这辆车");
    
            return null;
    
        }
    
    }
    
    //实例化工厂类
    public class CarFactoryTest {
        public static void main(String[] args) {
            CarFactory carfactory = new CarFactory();
            Car car1 = carfactory.getcar("Benz");
            car1.Brand();
    
            Car car2 = carfactory.getcar("Bmw");
            car2.Brand();
    
            Car car3 = carfactory.getcar("toyota");
            car3.Brand();
    
    
        }
    }
    
    执行结果:
    生产一辆奔驰
    生产一辆宝马
    生产一辆丰田

    5.2、抽象工厂模式

    抽象工厂模式是围绕一个超级工厂创建其他工厂,这个超级工厂是生产其他工厂的工厂。这种设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式地指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

    主要解决的问题:主要解决接口选择的问题。系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

    优点:当一个产品族中的多个对象被设计在一起工作时,它能保证客户端始终只使用一个产品族中的对象。

    缺点:产品族的扩展非常困难,要增加一个系列的某个产品,不但要在创造工厂里新增大量代码,还要在具体实现里加代码,产品族难以扩展。

    实现示例:还是以上一个实例为基础来说明抽象工厂模式。一家汽车集团公司,旗下有两个工厂,一家是生产国外品牌车(BENZ、BMW、TOYATA),另一家是生产国内品牌车(JAC、BYD、ROEWE)(这就相当于两个产品族)。集团有一家自营销售门店,采取顾客下单后再生产的经营策略(这就相当于系统只在一个时刻消费某一族的产品)。有两个接口:ForeignCar(国外品牌车)、DomesticCar(国内品牌车),汽车实现类:Benz、Bmw、Toyota、Jac、Byd、Roewe。一个工厂抽象类AbstractFactory,两个工厂类继承自这个抽象类:ForeigncarFactory、DomesticcarFactory。最后还有一个工厂生产者FactoryProducer和一个测试类AbstractFactoryTest。具体代码实现如下:

    国外品牌车接口及实现类

    public interface ForeignCar {
        public void Brand();
    }
    
    public class BNEZ implements ForeignCar{
        @Override
        public void Brand() {
            System.out.println("生产一辆奔驰");
        }
    }
    
    public class BMW implements ForeignCar{
        @Override
        public void Brand() {
            System.out.println("生产一辆宝马");
        }
    }
    
    public class TOYOTA implements ForeignCar{
        @Override
        public void Brand() {
            System.out.println("生产一辆丰田");
        }
    }

    国内品牌车接口及实现类

    public interface DomesticCar {
        public void Brand();
    }
    
    public class BYD implements DomesticCar{
        @Override
        public void Brand() {
            System.out.println("生产一辆比亚迪");
        }
    }
    
    public class JAC implements DomesticCar{
        @Override
        public void Brand() {
            System.out.println("生产一辆江淮");
        }
    }
    
    public class ROEWE implements DomesticCar{
        @Override
        public void Brand() {
            System.out.println("生产一辆荣威");
        }
    }
    

    工厂的抽象类及两个工厂实现类

    public abstract class AbstractFactory {
        public abstract ForeignCar getforeigncar(String brand);
    
        public abstract DomesticCar getdomesticcar(String brand);
    }
    
    public class ForeigincarFactory extends AbstractFactory{
        @Override
        public ForeignCar getforeigncar(String brand) {
            if (brand.equalsIgnoreCase("Benz")){
                return new BNEZ();
            }
            else if (brand.equalsIgnoreCase("Bmw")){
                return new BMW();
            }
            else if (brand.equalsIgnoreCase("Toyota")){
                return new TOYOTA();
            }
            else
                System.out.println("我们没有这个品牌授权");
            return null;
        }
    
        @Override
        public DomesticCar getdomesticcar(String brand) {
            return null;
        }
    }
    
    public class DomesticcarFactory extends AbstractFactory{
        @Override
        public ForeignCar getforeigncar(String brand) {
            return null;
        }
    
        @Override
        public DomesticCar getdomesticcar(String brand) {
            if (brand.equalsIgnoreCase("Jac")){
                return new JAC();
            }
            else if (brand.equalsIgnoreCase("Byd")){
                return new BYD();
            }
            else if (brand.equalsIgnoreCase("Roewe")){
                return new ROEWE();
            }
            else
                System.out.println("我们没有这个品牌授权");
            return null;
        }
    }

    工厂生产者及测试类

    public class FactoryProducer {
        public static AbstractFactory CreateFactory(String choice){
            if (choice.equalsIgnoreCase("ForeignCar")){
                return new ForeigincarFactory();
            }
            else if (choice.equalsIgnoreCase("DomesticCar")){
                return new DomesticcarFactory();
            }
            else
                System.out.println("我们没有这个工厂");
            return null;
        }
    }
    
    public class AbstractFactoryTest {
        public static void main(String[] args) {
            AbstractFactory abstractFactory1 = FactoryProducer.CreateFactory("ForeignCar");
    
            ForeignCar car1 = abstractFactory1.getforeigncar("toyota");
            car1.Brand();
    
            AbstractFactory abstractFactory2 = FactoryProducer.CreateFactory("DomesticCar");
    
            DomesticCar car2 = abstractFactory2.getdomesticcar("byd");
            car2.Brand();
        }
    }
    

    执行结果

    生产一辆丰田
    生产一辆比亚迪

    5.3、单例模式

    单例模式(Siningleton Pattern)属于创建型模式,它提供了一种创建对象的最佳方式。这种模式设计到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象的方式,可以直接访问,不需要实例化该类的对象。单例模式需要注意一下三点:

    1. 单例类只能有一个实例。
    2. 单例类必须自己创建自己的唯一实例。
    3. 单例类必须给所有其他对象提供这一实例。

    主要解决的问题:一个全局使用的类频繁地创建和销毁。当使用者想控制实例的数量,节省系统资源的时候就可以使用单例模式。

    优点

    1. 在内存中只有一个实例,减少了内存的开销,尤其是频繁地创建和销毁实例。
    2. 避免对资源的多重占用(比如写文件操作)。

    缺点

    1. 没有接口,不能继承,与单一职责原则冲突,一个类应该只关系内部实现逻辑,而不应该关注外部怎么来实例化。

    常见使用场景

    1. 要求生产唯一序列号。
    2. WEB中的计数器,不必每一次刷新都在数据库中加1,可以用单例先缓存起来。
    3. 创建一个对象需要消耗的资源过多,比如I/O与数据库的连接等。

    实现示例:SingletonObject是一个单例类,SingletonPatternTest是测试类。

    package SingletonDemo;
    
    public class SingletonObject {
        //创建这个类的唯一对象
        private static SingletonObject instance = new SingletonObject();
    
        //让构造函数私有化,确保外部不能调用这个类的构造器来实例化对象
        private SingletonObject(){
    
        }
    
        //获取这个唯一可用对象
        public static SingletonObject getInstance(){
            return instance;
        }
    
        public void showmessage(){
            System.out.println("这是单例模式");
        }
    }
    
    package SingletonDemo;
    
    public class SingletonPatternTest {
        public static void main(String[] args) {
            SingletonObject object = SingletonObject.getInstance();
    
            object.showmessage();
        }
    }
    

    单例模式的实现有多种方式(懒汉式和饿汉式有什么区别?为什么要这么叫?区别就在于创建对象的时机不同,懒汉式是当你需要时才去创建对象,而饿汉式是不管你需不需要,一开始就会创建对象):

    1. 懒汉式(线程不安全)
    2. 懒汉式(线程安全)
    3. 饿汉式
    4. 双检锁/双重校验锁(DCL,即double-checked locking)
    5. 登记式/静态内部类
    6. 枚举

    懒汉式(线程不安全)

    这种方式是最基本的实现方式,它最大的问题就是多线程不安全。原因是没有加锁synchronized,所以严格意义上来说它不属于单例模式,这种方式lazy loading(延迟加载)很明显,不要求线程安全,在多线程不能正常工作。

    public class Singleton{
        private static Singleton instance;
        
        private Singleton(){
        
        }
        
        public static Singleton getInstance(){
           if(instance == null){
                instance = new Singleton();
           }
           return instance;
        }
    }

    懒汉式(线程安全)

    这种方式具备很好的lazy loading,能够在多线程中很好的工作,但是效率很低,99%的情况下不需要同步。第一次调用才初始化,避免了内存浪费。但是它必须加锁才能够保证单例,加锁必定会影响效率。

    public class Singleton {
        private static Singleton instance;
    
        private Singleton(){
    
        }
    
        public static synchronized Singleton getInstance(){
            if (instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    }

    饿汉式(线程安全)

    它是基于classloader机制避免了多线程的同步问题,不过instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中都是调用getInstance()方法,但是也不能确定有其他的方式导致类装载,这是初始化显然没有达到lazy loading的效果。这种方式比较常用,但是容易产生垃圾对象。优点是没有加锁,执行效率高。缺点是类加载时就初始化,浪费内存。

    public class Singleton {
        private static Singleton instance = new Singleton();
    
        private Singleton(){
    
        }
    
        public static Singleton getInstance(){
            return instance;
        }
    }

    双检锁/双重校验锁(DCL,即double-checked locking)

    采用双检锁的方式,安全且多线程情况下能保持高性能。这种方式中getInstance()的性能对应用程序很关键。

    public class Singleton {
        private volatile static Singleton instance;
    
        private Singleton(){
    
        }
    
        public static Singleton getInstance(){
            if (instance == null){
                synchronized (Singleton.class){
                    if (instance == null){
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    
    }
    /**
     *为什么instance要使用volatile关键字修饰?
     *主要是为了防止发生指令重排
     *在 instance = new Singleton() 中主要有三步:
     *1、为Singleton()对象分配内存空间
     *2、初始化Singleton()对象
     *3、将instance指向Singleton()对象的内存地址
     *如果cpu进行优化,发生指令重排的话,那么步骤可能就会变成132
     *就有可能出现线程中A指令执行完1和3两步,另一个线程B发现instance不为空,于是直接拿去用,
     *但是这时Singleton()对象还没有初始化
    **/

    还有两种实现方式就不一一列出了,当明确要求实现lazy loading效果时,要使用“登记式/静态内部类”方式;如果涉及到反序列化创建对象时,可以尝试使用枚举。第一种和第二种懒汉式不推荐使用,一般都是使用第三种饿汉式。

    5.4、适配器模式

    适配器模式是作为两个不兼容的接口之间的桥梁,这种设计模式属于结构性模式,它结合了两个独立接口的功能。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实例子,读卡器是作为内存卡和笔记本之间的适配器,将内存卡插入读卡器,再将读卡器插入笔记本,这样就能通过笔记本来读取内存卡了。特别注意:适配器模式不是在详细设计时添加的,而是针对解决正在服役的项目的问题

    主要解决:主要解决在软件系统中,常常要将一些“现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。

    使用场景

    1. 系统需要使用现有的类,而此类的接口不符合系统的需要。
    2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
    3. 通过接口转换,将一个类插入另一个类系中(比如老虎和飞禽,现在要增加一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容老虎对象,实现飞的接口)。

    优点

    1. 可以让两个没有任何关联的类一起运行。
    2. 提高了类的复用。
    3. 增加了类的透明度。
    4. 灵活性好。

    缺点

    1. 过多的使用适配器会让系统非常的零乱,不易整体把控。比如,明明看到的是A接口,内部被适配成了B接口的实现,一个系统如果出现太多这样的问题,那么无异于异常灾难。因此如果不是特别有必要的话,可以不使用适配器,而是对系统直接进行重构。
    2. 由于Java至多只能继承一个类,所以至多只能适配一个适配器类,并且目标类必须是抽象类。

    实现示例:我们有一个Mp3接口和一个实现了该接口的实现类Mp3Player,默认情况下Mp3Player可以播放mp3格式的音频文件;还有另一个MediaPlayer接口和实现了该接口的实现类Mp4Player及VlcPlayer,这两个实现类分别可以播放vlc和mp4格式的文件。我们想让Mp3Player来播放vlc和mp4格式的音频文件,那么就需要创建一个实现了Mp3接口的适配器类MediaAdapter,并且使用Mp4Player对象来播放所需要的格式音频文件。

    Mp3Player使用适配器类MediaAdapter传递所需的音频类型,不需要知道能播放所需音频格式的实际类。我们在演示类AdapterPatternDemo来使用Mp3Player来播放各种音频格式。

    Mp3接口及MediaPlayer接口:

    public interface Mp3 {
        //传入参数:音频类型,音频文件名
        public void play(String audioType, String filename);
    }
    
    
    public interface MediaPlayer {
        public void playVlc(String filename);
    
        public void playMp4(String filename);
    }

    MediaPlayer接口的实现类Mp4Player、VlcPlayer:

    public class Mp4Player implements MediaPlayer{
        @Override
        public void playVlc(String filename) {
    
        }
    
        @Override
        public void playMp4(String filename) {
            System.out.println("Playing the MP4 file,filename: " + filename);
        }
    }
    
    
    public class VlcPlayer implements MediaPlayer {
        @Override
        public void playVlc(String filename) {
            System.out.println("Playing the Vlc file,filename: " + filename);
        }
    
        @Override
        public void playMp4(String filename) {
    
        }
    }

    适配器类MediaAdapter:

    public class MediaAdapter implements Mp3{
    
        //定义一个接口类型的引用变量来引来实现了该接口的实现类实例对象
        MediaPlayer mediaPlayer;
    
        public MediaAdapter(String audiotype){
            if (audiotype.equalsIgnoreCase("vlc")){
                mediaPlayer = new VlcPlayer();
            }else if (audiotype.equalsIgnoreCase("mp4")){
                mediaPlayer = new Mp4Player();
            }
        }
    
        @Override
        public void play(String audioType, String filename) {
            if (audioType.equalsIgnoreCase("vlc")){
                mediaPlayer.playVlc(filename);
            }else if (audioType.equalsIgnoreCase("mp4")){
                mediaPlayer.playMp4(filename);
            }
        }
    }

    Mp3接口的实现类Mp3Player:

    public class Mp3Player implements Mp3{
    
        //定义一个接口类型的引用变量来引来实现了该接口的实现类实例对象
        MediaAdapter mediaAdapter;
    
        @Override
        public void play(String audioType, String filename) {
            if (audioType.equalsIgnoreCase("mp3")){
                System.out.println("Playing the MP3 file,filename: " + filename);
            }else if (audioType.equalsIgnoreCase("mp4") || audioType.equalsIgnoreCase("vlc")){
                mediaAdapter = new MediaAdapter(audioType);
                mediaAdapter.play(audioType,filename);
            }else{
                System.out.println("该设备不支持这种音频格式播放!");
            }
        }
    }

    测试类AdapterPatternDemo:

    public class AdapterPatternDemo {
        public static void main(String[] args) {
            Mp3Player mp3Player = new Mp3Player();
    
            mp3Player.play("vlc","vlcfile1");
    
            mp3Player.play("mp4","mp4file1");
    
            mp3Player.play("mp3","mp3file1");
    
            mp3Player.play("xml","xmlfile1");
        }
    }
    

    执行结果:

    Playing the Vlc file,filename: vlcfile1
    Playing the MP4 file,filename: mp4file1
    Playing the MP3 file,filename: mp3file1
    该设备不支持这种音频格式播放!

    5.5、装饰器模式

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种设计模式属于结构型模式,它是作为现有类的一个包装。装饰器模式创建了一个装饰类,用来包装原有的类,并且在保持类方法签名完整性的前提下,提供额外的功能。

    主要解决:一般来说,我们为扩展一个类经常使用继承的方式实现,但由于继承为类引入了静态特性,并且随着扩展功能的增多,子类会很膨胀。所以当我们不想增加很多子类时我们就可以使用装饰器模式。

    优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

    缺点:多层装饰比较复杂。

    使用场景

    1. 扩展一个类的功能。
    2. 动态增加功能、动态撤销。

    实现示例:把一个形状装饰上不同的颜色,同时又不改变形状类。创建一个Shape接口和实现了该接口的实体类,然后创建一个实现了Shape接口的抽象装饰类ShapeDecorator,并把Shape对象作为它的实例变量。RedShapeDecorator是实现了ShapeDecorator的实体类。我们在演示类DecoratorPatternDemo中使用RedShapeDecorator来装饰Shape对象。

    Shape接口及实现类Circle、Rectangle:

    public interface Shape {
        public void draw();
    }
    
    
    public class Circle implements Shape{
        @Override
        public void draw() {
            System.out.println("这是一个圆形!");
        }
    }
    
    
    public class Rectangle implements Shape{
        @Override
        public void draw() {
            System.out.println("这是一个长方形!");
        }
    }

    抽象的装饰器类ShapeDecorator:

    public abstract class ShapeDecorator implements Shape{
        protected Shape decoratorShape;
    
        public ShapeDecorator(Shape decoratorShape){
            this.decoratorShape = decoratorShape;
        }
    
        public void draw(){
            decoratorShape.draw();
        }
    }

    抽象类的实现类RedShapeDecorator:

    public class RedShapeDecorator extends ShapeDecorator{
        public RedShapeDecorator(Shape decoratorShape) {
            super(decoratorShape);
        }
    
        @Override
        public void draw(){
            decoratorShape.draw();
            setRedBorder(decoratorShape);
        }
    
        public void setRedBorder(Shape decoratedShape){
            System.out.println("边框颜色:红色!");
        }
    }

    测试类DecoratorPatternDemo:

    public class DecoratoePatternDemo {
        public static void main(String[] args) {
    
            Shape circle = new Circle();
    
            Shape redcircle = new RedShapeDecorator(new Circle());
    
            Shape redrectangle = new RedShapeDecorator(new Rectangle());
            System.out.println("圆形边框颜色正常!");
            circle.draw();
    
            System.out.println("\n红色边框的圆形");
            redcircle.draw();
    
            System.out.println("\n红色边框的长方形");
            redrectangle.draw();
        }
    }

    执行结果:

    圆形边框颜色正常!
    这是一个圆形!
    
    红色边框的圆形
    这是一个圆形!
    边框颜色:红色!
    
    红色边框的长方形
    这是一个长方形!
    边框颜色:红色!

    5.6、代理模式

    在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种设计模式属于结构型模式。在代理模式中我们创建具有现有对象的对象,以便向外界提供功能接口。

    主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。主要解决思路就是加上一个中间层

    何时使用:想在访问一个类时做一些控制。

    优点

    1. 职责清晰。
    2. 高扩展性。
    3. 智能化。

    缺点

    1. 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
    2. 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

    常见使用场景

    1. 远程代理、虚拟代理、Copy-on-Write代理、保护代理、Cache代理、防火墙代理、同步化代理、智能引用代理。
    2. Spring aop。

    注意事项

    1. 和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
    2. 和装饰器模式的区别:装饰器模式是为了增强功能,而代理模式是为了加以控制。

    实现示例:经纪人和明星就是一个很明显的代理关系。一家公司想请明星来公司参加开业活动,那么公司一定是和经纪人取得联系,通过经纪人来安排明星出席开业活动,就相当于通过经纪人来控制明星的行为。定义一个接口Activity,明星类Star和经纪人Proxy都实现了这个接口,然后在演示类ProxyPatternDemo中来使用Proxy对象控制Star对象(经纪人接下这个活动实际是由明星来参加)。

    Activity接口及实现类Star、Proxy:

    public interface Activity {
        public void campaign();
    }
    
    
    public class Star implements Activity{
    
        private String activityname;
    
        public Star(String activityname){
            this.activityname = activityname;
        }
        @Override
        public void campaign() {
            System.out.println("我是明星,我来参加" + activityname +"活动!");
        }
    }
    
    
    public class Proxy implements Activity{
    
        private Star star;
        private String activityname;
    
        public Proxy(String activityname){
            this.activityname = activityname;
        }
        @Override
        public void campaign() {
            if (star == null){
                star = new Star(activityname);
            }
            star.campaign();
        }
    }
    

    演示类ProxyPatternDemo:

    public class ProxyPatternDemo {
        public static void main(String[] args) {
            Activity activity = new Proxy("九九隆开业庆典");
            
            //经纪人接下这个活动,由明星来参加
            activity.campaign();
        }
    }
    

    执行结果:

    我是明星,我来参加九九隆开业庆典活动!

    5.7、策略模式

    在策略模式(Strategy Pattern)中,一个类的行为或算法可以在运行时更改。这种设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的context对象,策略对象改变context对象的执行算法。

    主要解决:在有多种算法相似的情况下,使用if...else所带来的复杂和难以维护。

    何时使用:一个系统中有很多很多类,而区分它们的只是他们直接的行为。

    如何解决:将这些算法封装成一个一个的类,任意地替换。关键是要实现同一个接口

    优点

    1. 算法可以自由切换。
    2. 避免使用多重条件判断。
    3. 扩展性良好。

    缺点

    1. 策略类会增多。
    2. 策略类会对外暴露。

    常见使用场景

    1. 旅游出行的交通方式,选择骑自行车、坐汽车、搭乘飞机,每一种出行方式就是一种策略。
    2. 如果在一个系统中有许多类,它们之间的却别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多种行为中选择一种行为。
    3. 一个系统需要动态地在几种算法中选择一种。
    4. 如果一个对象有很多行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

    注意事项如果一个系统的策略多于4个,就需要考虑使用混合模式,解决策略类膨胀的问题

    实现示例:定义一个数字运算接口Strategy,以及三个实现了该接口的实现类OperationAdd、OperationSub、OperationMul,这三个实现类代表了三个不同的算法策略。定义一个Context类来选择执行策略,并在StrategyPatternDemo演示类中进行演示。

    Strategy接口以及三个实现类:

    public interface Strategy {
        public int doOperation(int num1, int num2);
    }
    
    
    public class OperationAdd implements Strategy{
        @Override
        public int doOperation(int num1, int num2) {
            return num1 + num2;
        }
    }
    
    
    public class OperationSub implements Strategy{
        @Override
        public int doOperation(int num1, int num2) {
            return num1 - num2;
        }
    }
    
    
    public class OperationMul implements Strategy{
        @Override
        public int doOperation(int num1, int num2) {
            return num1 * num2;
        }
    }

    策略选择类Context:

    public class Context {
    
        private Strategy strategy;
    
        public Context(Strategy strategy){
            this.strategy = strategy;
        }
    
        public int executeStrategy(int num1, int num2){
            return strategy.doOperation(num1, num2);
        }
    }

    演示类StrategyPatternDemo:

    public class StrategyPatternDemo {
        public static void main(String[] args) {
            Context context = new Context(new OperationAdd());
            System.out.println("3 + 8 = " + context.executeStrategy(3, 8));
    
            context = new Context(new OperationSub());
            System.out.println("3 - 8 = " + context.executeStrategy(3, 8));
    
            context = new Context(new OperationMul());
            System.out.println("3 * 8 = " + context.executeStrategy(3, 8));
        }
    }

    执行结果:

    3 + 8 = 11
    3 - 8 = -5
    3 * 8 = 24

    5.8、观察者模式

    当对象存在一对多关系时,则使用观察者模式(Observer Pattern)。比如当一个对象被修改时,则自动通知它的依赖对象。观察者模式属于行为型模式(定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新)。比如说在拍卖时,拍卖师要观察出价最高的人,然后通知给其他参与竞价的人。

    主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

    关键代码:在抽象类中有一个ArrayList存放观察者们。

    优点

    1. 观察者和被观察者是抽象耦合的。
    2. 建立一套触发机制。

    缺点:

    1. 如果一个被观察者有很多直接或间接的观察者的话,将所有观察者都通知到是一件很麻烦的事,要花费很多时间。
    2. 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间的循环调用,可能调至系统崩溃。
    3. 观察者模式没有相应的机制让观察者知道观察目标是如何发生变化的,而仅仅是知道观察目标发生了变化。

    常见使用场景

    1. 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们独立地改变和复用。
    2. 一个对象的改变将导致其他一个或多个对象也发生变化,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
    3. 一个对象必须通知其他对象,而不知道这些对象是谁。

    注意事项:

    1. Java中已经有了对观察者模式的支持类。
    2. 避免循环引用。
    3. 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

    实现示例:观察者模式使用三个类Subject、Observer、Client。Subject对象带有绑定观察者到Clinet对象和从Client对象解绑观察者的方法。我们创建Subject类、Observer抽象类和扩展了抽象类Observer的实体类。在演示类ObserverPatternDemo中使用Subject和实体类对象来演示观察者模式。

    观察目标Subject类:

    import java.util.ArrayList;
    import java.util.List;
    
    //Subject是观察目标,当subject的私有属性值state发生改变时,它的观察者们也做出相应的改变
    public class Subject {
        //将观察者们添加到ArrayList集合中
        private List<Observer> observers = new ArrayList<Observer>();
    
        //表示要改变的值
        private int state;
    
        public int getState(){
            return state;
        }
    
        public void setState(int state){
            this.state = state;
            notifyAllObservers();
        }
    
        //增加观察者对象
        public void attach(Observer observer){
            observers.add(observer);
        }
    
        //遍历ArrayList中的观察者对象并执行观察者们的update()方法
        public void notifyAllObservers(){
            for (Observer observer : observers){
                observer.update();
            }
        }
    }

    观察者模板,抽象类Observer:

    //构建观察者模板
    public abstract class Observer {
        //这是观察目标
        protected Subject subject;
    
        //更新方法
        public abstract void update();
    }

    观察者BinaryObserver类:

    public class BinaryObserver extends Observer{
    
        //构造函数中传入观察目标,并将自己添加到观察目标subject保存的观察者集合中
        public BinaryObserver(Subject subject){
            this.subject = subject;
            this.subject.attach(this);
        }
    
        //Integer.toBinaryString()就是将输入的数字转换成二进制数,但是转换输出的是String类型的字符串
        @Override
        public void update() {
            System.out.println("转成二进制数是 :" + Integer.toBinaryString(subject.getState()));
        }
    }

    观察者OctalObserver类:

    public class OctalObserver extends Observer{
    
        public OctalObserver(Subject subject){
            this.subject = subject;
            this.subject.attach(this);
        }
    
        //Integer.toOctalString()是将十进制数转换成八进制数并以字符串的类型输出
        @Override
        public void update() {
            System.out.println("转成八进制数是 :" + Integer.toOctalString(subject.getState()));
        }
    }

    观察者HexaObserver类:

    public class HexaObserver extends Observer{
    
        public HexaObserver(Subject subject){
            this.subject = subject;
            this.subject.attach(this);
        }
    
        //Integer.toHexString()是将十进制数转成十六进制并以字符串类型输出
        //toUpperCase()是将小写字符转换成大写字符
        @Override
        public void update() {
            System.out.println("转成十六进制数是 :" + Integer.toHexString(subject.getState()).toUpperCase());
        }
    }

    演示类ObserverPatternDemo:

    public class ObserverPatternDemo {
        public static void main(String[] args) {
            Subject subject = new Subject();
    
            new BinaryObserver(subject);
            new OctalObserver(subject);
            new HexaObserver(subject);
    
            System.out.println("第一次state值改变为: 15");
            subject.setState(15);
            System.out.println("\n第二次state值改变为: 10");
            subject.setState(10);
        }
    }

    执行结果:

    第一次state值改变为: 15
    转成二进制数是 :1111
    转成八进制数是 :17
    转成十六进制数是 :F
    
    第二次state值改变为: 10
    转成二进制数是 :1010
    转成八进制数是 :12
    转成十六进制数是 :A

     

    展开全文
  • ps的三种色彩模式

    千次阅读 2018-07-13 15:17:10
    在Photoshop 中常见的有三种色彩模式,记录在下面。一:RGB颜色它是Photoshop中最常用的一模式,是由红、绿和蓝三种颜色按照不同比例和轻强度混合出来绝大多数光谱中的颜色。各通道取色范围为0~255,所以这256级...

    在Photoshop 中常见的有三种色彩模式,记录在下面。

    一:RGB颜色

    它是Photoshop中最常用的一种模式,是由红、绿和蓝三种颜色按照不同比例和轻强度混合出来绝大多数光谱中的颜色。各通道取色范围为0~255,所以这256级的RGB颜色总共能组合出约1678万种色彩,通常也被简称为1600万色或者千万色。RGB颜色为加色模式,颜色越多越亮。

    二:CMYK颜色模式

    它是基于纸张上打印的油墨对色光吸收的结果而形成的一种色彩模式,当白光照射到半透明的油膜上时,油墨载体将吸收一部分光谱,没有吸收的颜色就反射回我们的眼睛。该颜色模式称为减色模式,其中C=青色,M=洋红色,Y=黄色,K=黑色,它是减色模式,颜色越混越深。该模式更适合印刷方面。

    三:LAB颜色模式

    它由三种要素组成,(L)是亮度通道,a和b是另外两个颜色通道,a包括的颜色是从深绿色(低亮度值)到灰色(中亮度值)再到黄色(高亮度值);而b是从亮蓝色(低亮度值)到灰色(中亮度值)再到黄色(高亮度值)。因此,图像的颜色和亮度被区分开来,单独提高像素的亮度将会减少像素颜色纯度的变化,这有助于在维持原有色彩效果的前提下使图像更清晰,所以在Lab模式下能够较好的对图像进行锐化。


    展开全文
  • 种色彩模式简介

    千次阅读 2017-10-25 21:42:32
    数字世界中表示颜色的一算法成为色彩模式。在数字世界中,为了表示各种颜色,人们通常将颜色划分为若干分量。由于成色原理的不同,决定了显示器、投影仪、扫描仪这类靠色光直接合成颜色颜色设备和打印机、印刷机...
  • 常见视频渲染模式介绍

    千次阅读 2017-10-09 11:00:00
    1、.视频覆盖: 大概在1995年,当mpeg1(VCD)回放在PC上...它就是在显卡上用硬件完成同时显示几个视频的功能(拉伸,颜色空间变换,等等...)的技术,这样就可以在pc上流畅的播放动画而不占用cpu。视频覆盖的画面...
  • 种常见的UITabBarController+UINavigationController模式
  • 对几种常见设计模式的理解

    千次阅读 2015-08-31 10:58:49
    对几种常见设计模式的理解 设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 最早提出“设计模式”概念的...
  • 构造函数模式 /** * 构造一个动物的函数 */ function Animal(name, color){ this.name = name; this.color = color; this.getName = function(){ return this.name; } } // 实例一个对象 var cat = new ...
  • photoshop中常见色彩模式(一)

    千次阅读 2010-01-22 12:58:00
    1.RGB模式:由红,绿和蓝三原色混合而成的色彩模式,是photoshop中最常见色彩模式。红,绿蓝三原色的取值范围都是0~255,将这三原色进行各种比例的调和即可产生16777216(256x256x256)种颜色,可以达到很好...
  • 常见方法是将采用单例设计模式的类的构造方法私有化,并在其内部产生该类的实例化对象,并将其封装成private static类型,时定义一个静态方法返回该类的实例。 根据实例对象按需产生或是加载类时就产生可分为懒汉式...
  • 本文笔者将对一些APP的夜间模式进行分析,总结夜间模式常见的切换方式,以及从简单到复杂的几夜间模式设计方法。一、夜间模式的由来和作用最早应该是智能手机的普及大大延迟了人们的睡眠时间,大家都习惯晚上躺在...
  • 位图模式的图像也叫作黑白图像,因为位图模式使用两种颜色值(黑.白) 来表示图像中的像素。此模式所占用的磁盘空间最少,每一个像素都是用1位的分辨率来记录的。要将图像转换成位图模式,必须先将其转换为灰度模式。 ...
  • 目前主流的App框架 如:qq,微信,UITabBarController中嵌套...第一方式: 导航控制器上的title不能和tabbar上面的同步,需要手动单独设置,比如可以在控制器的viewWillAppear:方法里面设置....
  • 印刷知识CMYK颜色模式印刷模式

    千次阅读 2008-05-09 14:08:00
    RGB色彩和CMYK色彩是两不同的色彩模式。 RGB色彩模式是最基础的色彩模式,所以RGB色彩模式是一个重要的模式。只要在电脑屏幕上显示的图像,就一定是以RGB模式。因为显示器的物理结构就是遵循RGB的。 CMYK也称作...
  • RAID模式有很多,不过最常见的只有RAID 0/1/5/10几,不同工作模式有很大差异,下面先为大家介绍一下不同模式的区别。 ●RAID 0:速度至上 没有备份 首先我们来看一下对普通玩家意义最大的RAID 0,刨去专业定义...
  • 在PS软件中打开一张图片,可以看到此时图片的色彩模式为“cmyk”模式,下面我们需要将其转换为“RGB”模式。 点击PS上方菜单栏中的图像-模式,选择RGB颜色即可。 现在我们可以看到,图片已经转换为“RGB”模式的...
  • 色彩模式

    2014-04-01 11:16:00
    色彩模式是数字世界中表示颜色的一算法。在数字世界中,为了表示各种颜色,人们通常将颜色划分为若干分量。由于成色原理的不同,决定了显示器、投影仪、扫描仪这类靠色光直接合成颜色颜色设备和打印机、印刷机...
  • AGRB色彩模式

    2018-05-24 14:43:02
    ARGB是一种色彩模式,也就是RGB色彩模式附加上Alpha(透明度)通道,常见于32位位图的存储结构。
  • 科普——色彩模式

    2017-10-22 22:38:04
    下面给出常见种色彩模式简单的介绍。RGB色彩模式光学三原色——红、绿、蓝,以及其组合重叠出的颜色,几乎涵盖了人类所有的视觉范围,组成了我们所看到的整个世界。 RGB色彩模式广泛应用于电脑、电视的液晶屏等。...
  • OpenCV学习心得——基础篇——三种常见色彩空间——RGB、GRAY、HSV颜色识别 FOR THE SIGMA FOR THE GTINDER FOR THE ROBOMASTER 简介: 这一系列的学习心得第一轮将参考《学习OpenCV3》一书 操作系统版本:Ubuntu...
  • 色彩模式科普

    2017-10-24 17:09:30
    色彩模式是数字世界中表示颜色的一算法。在数字世界中,为了表示各种颜色,人们通常将颜色划分为若干分量。由于成色原理的不同,决定了显示器、投影仪、扫描仪这类靠色光直接合成颜色颜色设备和打印机、印刷机这...
  • ps的颜色模式有哪些目前,在图像文件中最常用的颜色模式主要有RGB颜色、CMYK颜色、Lab颜色、灰度模式、位图模式等。下面小编就来简要介绍一下各种颜色模式的特点。RGB模式用红(R)、绿(G)、蓝(B)三色光创建颜色。扫描...
  • ARGB:指的是一种色彩模式,里面A代表Alpha,R表示red,G表示green,B表示blue。 自然界中所有的可见色都是由红、绿、蓝组成的,所以红、绿、蓝又称为三原色,每个原色都存储着所表示颜色的信息值 A-&gt;alpha...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 87,281
精华内容 34,912
关键字:

常见的5种颜色模式