策略模式

定义

策略模式定义了一系列的算法,并将每个算法封装起来,而且使它们还可以相互替换。

使用场景

  • 针对同一种问题的多种处理方式,仅仅是具体行为有差别时
  • 需要安全地封装同一种类型的操作的时候
  • 出现同一抽象类有多个子类,而又需要使用if-else 或者 swith-case来选择具体子类时

理解

其实就是当有多种策略来解决问题的时候,我们封装每一种方法,然后让它可以相互替换,这样增强程序的可拓展性、可维护性;

实现方法

实现方法其实一般就是抽象出策略接口来,每一种具体的策略去实现接口,这样封装了每个策略;实现了同一个接口,所以可以相互替换

image

举个栗子

这个模式其实非常常见,比如图片加载框架中,图片可以被缓存在内存中、缓存在本地文件中、保存到服务器;那么获取图片的时候,也可以从缓存中找,从本地文件找,从服务器取;开闭原则这篇文章里面已经讲过了,这其实就是一个策略模式的例子

重新举一个例子,微信跳一跳在2017年年末的时候很火,跳一跳外挂也很火,之前也根据颜色识别写了Android上实现微信跳一跳外挂;其中最主要的就是颜色识别,计算两种颜色的相似度,算法大概试了三种;

首先抽象出颜色相似的接口

public interface LikeColor {
    /**
     * 两种颜色是否相似
     *
     * @param color1
     * @param color2
     * @param aberration 允许差异值
     * @return
     */
    boolean isLike(int color1, int color2, double aberration);
}

实现HSV颜色空间算法

public class HsvColorLike implements LikeColor {

    @Override
    public boolean isLike(int color1, int color2, double aberration) {
        if (hsvAberration(color1, color2) <= aberration) {
            return true;
        }
        return false;
    }
    
    /**
     * HSV颜色空间计算颜色距离
     */
    public static double hsvAberration(int color1, int color2) {
        float[] tempHSV1 = new float[3];
        Color.colorToHSV(color1, tempHSV1);
        float[] tempHSV2 = new float[3];
        Color.colorToHSV(color2, tempHSV2);
        HSV hsv1 = new HSV();
        hsv1.H = tempHSV1[0];
        hsv1.S = tempHSV1[1];
        hsv1.V = tempHSV1[2];
        HSV hsv2 = new HSV();
        hsv2.H = tempHSV2[0];
        hsv2.S = tempHSV2[1];
        hsv2.V = tempHSV2[2];
        return HSV.distanceOf(hsv1, hsv2);
    }

    
    public static class HSV {
        public float H;
        public float S;
        public float V;

        //self-defined
        private static final double R = 100;
        private static final double angle = 30;
        private static final double h = R * Math.cos(angle / 180 * Math.PI);
        private static final double r = R * Math.sin(angle / 180 * Math.PI);

        /**
         * HSV颜色空间计算颜色距离
         * HSV是个六棱锥模型,这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)
         *
         * @param hsv1
         * @param hsv2
         * @return
         */
        public static double distanceOf(HSV hsv1, HSV hsv2) {
            double x1 = r * hsv1.V * hsv1.S * Math.cos(hsv1.H / 180 * Math.PI);
            double y1 = r * hsv1.V * hsv1.S * Math.sin(hsv1.H / 180 * Math.PI);
            double z1 = h * (1 - hsv1.V);
            double x2 = r * hsv2.V * hsv2.S * Math.cos(hsv2.H / 180 * Math.PI);
            double y2 = r * hsv2.V * hsv2.S * Math.sin(hsv2.H / 180 * Math.PI);
            double z2 = h * (1 - hsv2.V);
            double dx = x1 - x2;
            double dy = y1 - y2;
            double dz = z1 - z2;
            return Math.sqrt(dx * dx + dy * dy + dz * dz);
        }
    }
}

实现LAB颜色空间算法

public class LabColorLike implements LikeColor {

    @Override
    public boolean isLike(int color1, int color2, double aberration) {
        if (labAberration(color1, color2) <= aberration) {
            return true;
        }
        return false;
    }
    
    /**
     * LAB颜色空间计算色差,基于人眼对颜色的感知,
     * 可以表示人眼所能感受到的所有颜色。
     * L表示明度,A表示红绿色差,B表示蓝黄色差
     */
    public static int labAberration(int color1, int color2) {
        int r1 = Color.red(color1); // 取高两位
        int g1 = Color.green(color1);// 取中两位
        int b1 = Color.blue(color1);// 取低两位
        int r2 = Color.red(color2); // 取高两位
        int g2 = Color.green(color2);// 取中两位
        int b2 = Color.blue(color2);// 取低两位

        int rmean = (r1 + r2) / 2;
        int r = r1 - r2;
        int g = g1 - g2;
        int b = b1 - b2;
        return (int) Math.sqrt((2 + rmean / 256) * (Math.pow(r, 2)) + 4 * (Math.pow(g, 2)) + (2 + (255 - rmean) / 256) * (Math.pow(b, 2)));
    }
}

最后自己也写了一个基于RGB的简单的颜色算法

public class RgbColorLike implements LikeColor {
    @Override
    public boolean isLike(int color1, int color2, double aberration) {
        return baseLike(color1, color2, aberration);
    }


    /**
     * @param color1 第一种颜色
     * @param color2 第二种颜色
     * @return
     */
    public  boolean baseLike(int color1, int color2, double aberration) {
        int red = Color.red(color1); // 取高两位
        int green = Color.green(color1);// 取中两位
        int blue = Color.blue(color1);// 取低两位
        int red2 = Color.red(color2); // 取高两位
        int green2 = Color.green(color2);// 取中两位
        int blue2 = Color.blue(color2);// 取低两位
        if (red == red2 && green == green2 && blue == blue2) {
            return true;
        }
        if ((Math.abs(red - red2) < aberration && Math.abs(green - green2) < aberration && Math.abs(blue - blue2) < aberration) &&
                (Math.abs(red - red2) + Math.abs(green - green2) + Math.abs(blue - blue2)) < aberration * 2.5) {
            return true;
        }
        return false;
    }


    /**
     * 简单的RGB颜色判断
     *
     * @return
     */
    public static void rgbAberration(int color1, int color2) {
        int red1 = Color.red(color1); // 取高两位
        int green1 = Color.green(color1);// 取中两位
        int blue1 = Color.blue(color1);// 取低两位
        int red2 = Color.red(color2); // 取高两位
        int green2 = Color.green(color2);// 取中两位
        int blue2 = Color.blue(color2);// 取低两位

        int red = Math.abs(red1 - red2);
        int green = Math.abs(green1 - green2);
        int blue = Math.abs(blue1 - blue2);
        LogUtils.e("RGB颜色判断:色差:" + red + "," + green + "," + blue + ",all:" + (red + green + blue));
    }

}

对比颜色的时候就可以随意调用任意一种算法

 /**
     * 对比颜色
     *
     * @param color1
     * @param color2
     * @param aberration
     * @return
     */
    public static boolean colorLike(int color1, int color2, int aberration, LikeColor labLike) {
        return labLike.isLike(color1, color2, aberration);
    }

总结

理解起来很简单,平时应该也会频繁用到,遵循单一职责原则和开闭原则,增强了程序的可拓展性,可维护性。

你可能感兴趣的:(策略模式)