设计模式

一. 面试常问在项目中你用过哪些设计模式?

      1.策略模式

      (1)定义: 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可

                           以相互替换,让算法独立于使用它的客户而独立变化。

      (2)策略模式的应用场景

             (a)针对同一类型问题的多种处理方式,仅仅是具体行为有差别时; 

             (b)需要安全地封装多种同一类型的操作时; 

             (c)出现同一抽象类有多个子类,而又需要使用 if-else 或者 switch-case 来选择

                      具体子类时。

      (3)策略模式涉及3个角色

             (a)环境(Context)角色:持有一个Strategy的引用。

             (b)抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实

                      现。此角色给出所有的具体策略类所需的接口。

            (c)具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

            (d)关于(a)(b)(c)概念举例的说明

                     抽象策略类:

public interface Strategy {
    /**
     * 策略方法
     */
    public void strategyInterface();
}

 

                     具体策略类:

public class ConcreteStrategyA implements Strategy {

    @Override
    public void strategyInterface() {
        //相关的业务
    }

}

 

public class ConcreteStrategyB implements Strategy {

    @Override
    public void strategyInterface() {
        //相关的业务
    }

}

 

                     环境角色类:

public class Context {
    //持有一个具体策略的对象
    private Strategy strategy;
    /**
     * 构造函数,传入一个具体策略对象
     * @param strategy    具体策略对象
     */
    public Context(Strategy strategy){
        this.strategy = strategy;
    }
    /**
     * 策略方法
     */
    public void contextInterface(){

        strategy.strategyInterface();
    }

}

 

      (4)策略模式应用实例

               假设鹅厂推出了3种会员,分别为会员,超级会员以及金牌会员,还有就是普通玩

               家,针对不同类别的玩家,购买《王者农药》皮肤有不同的打折方式,并且一个顾

               客每消费10000就增加一个级别,那么我们就可以使用策略模式,因为策略模式描

              述的就是算法的不同,这里我们举例就采用最简单的,以上四种玩家分别采用原价

             (普通玩家),九折,八折和七价的收钱方式。

              那么我们首先要有一个计算价格的策略接口:

public interface CalPrice {
    //根据原价返回一个最终的价格
    Double calPrice(Double orgnicPrice);
}

 

              下面是4种玩家的计算方式的实现:

public class Orgnic implements CalPrice {

    @Override
    public Double calPrice(Double orgnicPrice) {
        return orgnicPrice;
    }
}

 

public class Vip implements CalPrice {
    @Override
    public Double calPrice(Double orgnicPrice) {
        return orgnicPrice * 0.9;
    }
}

 

public class SuperVip implements CalPrice {
    @Override
    public Double calPrice(Double orgnicPrice) {
        return orgnicPrice * 0.8;
    }
}

 

public class GoldVip implements CalPrice {
    @Override
    public Double calPrice(Double orgnicPrice) {
        return orgnicPrice * 0.7;
    }
}

 

              我们看客户类,我们需要客户类帮我们完成玩家升级的功能。

public class Player {
    private Double totalAmount = 0D;//客户在鹅厂消费的总额
    private Double amount = 0D;//客户单次消费金额
    private CalPrice calPrice = new Orgnic();//每个客户都有一个计算价格的策略,初始都是普通计算,即原价

    //客户购买皮肤,就会增加它的总额
    public void buy(Double amount) {
        this.amount = amount;
        totalAmount += amount;
        if (totalAmount > 30000) {//30000则改为金牌会员计算方式
            calPrice = new GoldVip();
        } else if (totalAmount > 20000) {//类似
            calPrice = new SuperVip();
        } else if (totalAmount > 10000) {//类似
            calPrice = new Vip();
        }
    }

    //计算客户最终要付的钱
    public Double calLastAmount() {
        return calPrice.calPrice(amount);
    }
}

 

              接下来是客户端调用,系统会帮我们自动调整收费策略。

public class Client {
    public static void main(String[] args) {
        Player player = new Player();
        player.buy(5000D);
        System.out.println("玩家需要付钱:" + player.calLastAmount());
        player.buy(12000D);
        System.out.println("玩家需要付钱:" + player.calLastAmount());
        player.buy(12000D);
        System.out.println("玩家需要付钱:" + player.calLastAmount());
        player.buy(12000D);
        System.out.println("玩家需要付钱:" + player.calLastAmount());
    }
}

 

              运行以后会发现,第一次是原价,第二次是九折,第三次是八折,最后一次则是

              七价。这样设计的好处是,客户不再依赖于具体的收费策略,依赖于抽象永远是

              正确的。

              在上面的基础上,我们可以使用简单工厂来稍微进行优化:

public class CalPriceFactory {
    private CalPriceFactory(){}
    //根据客户的总金额产生相应的策略
    public static CalPrice createCalPrice(Player customer){
        if (customer.getTotalAmount() > 30000) {//3000则改为金牌会员计算方式
            return new GoldVip();
        }else if (customer.getTotalAmount() > 20000) {//类似
            return new SuperVip();
        }else if (customer.getTotalAmount() > 10000) {//类似
            return new Vip();
        }else {
            return new Orgnic();
        }
    }
}

 

              这样就将制定策略的功能从客户类分离了出来,我们的客户类可以变成这样:

public class Player {
    private Double totalAmount = 0D;//客户在鹅厂消费的总额
    private Double amount = 0D;//客户单次消费金额
    private CalPrice calPrice = new Orgnic();//每个客户都有一个计算价格的策略,初始都是普通计算,即原价

    //客户购买皮肤,就会增加它的总额
    public void buy(Double amount) {
        this.amount = amount;
        totalAmount += amount;
        /* 变化点,我们将策略的制定转移给了策略工厂,将这部分责任分离出去 */
        calPrice = CalPriceFactory.createCalPrice(this);
    }

    //计算客户最终要付的钱
    public Double calLastAmount() {
        return calPrice.calPrice(amount);
    }

    public Double getTotalAmount() {
        return totalAmount;
    }
}

 

              虽然结合简单工厂模式,我们的策略模式灵活了一些,但不免发现在工厂中多

              了if-else判断,也就是如果增加一个会员类别,我又得增加一个else-if语句,这

              是简单工厂的缺点,对修改开放。那有什么方法,可以较好的解决这个问题呢?

              那就是使用注解, 所以我们需要给注解加入属性上限和下限,用来表示策略生

              效的区间,用来解决总金额判断的问题。

              首先我们做一个注解,这个注解是用来给策略添加的,当中可以设置它的上下

              限:

//这是有效价格区间注解,可以给策略添加有效区间的设置
@Target(ElementType.TYPE)//表示只能给类添加该注解
@Retention(RetentionPolicy.RUNTIME)//这个必须要将注解保留在运行时
public @interface PriceRegion {
    int max() default Integer.MAX_VALUE;
    int min() default Integer.MIN_VALUE;
}

 

              可以看到,我们只是使用这个注解来声明每一个策略的生效区间,于是对策略

              进行修改:

@PriceRegion(max = 10000)
public class Orgnic implements CalPrice {

    @Override
    public Double calPrice(Double orgnicPrice) {
        return orgnicPrice;
    }
}

 

@PriceRegion(max=20000)
public class Vip implements CalPrice {
    @Override
    public Double calPrice(Double orgnicPrice) {
        return orgnicPrice * 0.9;
    }
}

 

@PriceRegion(min=20000,max=30000)
public class SuperVip implements CalPrice {
    @Override
    public Double calPrice(Double orgnicPrice) {
        return orgnicPrice * 0.8;
    }
}

 

@PriceRegion(min=3000)
public class GoldVip implements CalPrice {
    @Override
    public Double calPrice(Double orgnicPrice) {
        return orgnicPrice * 0.7;
    }
}

              接下来就是在策略工厂中去处理注解:

public class CalPriceFactory {
    private static final String CAL_PRICE_PACKAGE = "com.example.stragedemo";//这里是一个常量,表示我们扫描策略的包

    private ClassLoader classLoader = getClass().getClassLoader();

    private List<Class<? extends CalPrice>> calPriceList;//策略列表

    //根据玩家的总金额产生相应的策略
    public CalPrice createCalPrice(Player player) {
        //在策略列表查找策略
        for (Class<? extends CalPrice> clazz : calPriceList) {
            PriceRegion validRegion = handleAnnotation(clazz);//获取该策略的注解
            //判断金额是否在注解的区间
            if (player.getTotalAmount() > validRegion.min() && player.getTotalAmount() < validRegion.max()) {
                try {
                    //是的话我们返回一个当前策略的实例
                    return clazz.newInstance();
                } catch (Exception e) {
                    throw new RuntimeException("策略获得失败");
                }
            }
        }
        throw new RuntimeException("策略获得失败");
    }

    //处理注解,我们传入一个策略类,返回它的注解
    private PriceRegion handleAnnotation(Class<? extends CalPrice> clazz) {
        Annotation[] annotations = clazz.getDeclaredAnnotations();
        if (annotations == null || annotations.length == 0) {
            return null;
        }
        for (int i = 0; i < annotations.length; i++) {
            if (annotations[i] instanceof PriceRegion) {
                return (PriceRegion) annotations[i];
            }
        }
        return null;
    }

    //单例
    private CalPriceFactory() {
        init();
    }

    //在工厂初始化时要初始化策略列表
    private void init() {
        calPriceList = new ArrayList<Class<? extends CalPrice>>();
        File[] resources = getResources();//获取到包下所有的class文件
        Class<CalPrice> calPriceClazz = null;
        try {
            calPriceClazz = (Class<CalPrice>) classLoader.loadClass(CalPrice.class.getName());//使用相同的加载器加载策略接口
        } catch (ClassNotFoundException e1) {
            throw new RuntimeException("未找到策略接口");
        }
        for (int i = 0; i < resources.length; i++) {
            try {
                //载入包下的类
                Class<?> clazz = classLoader.loadClass(CAL_PRICE_PACKAGE + "." + resources[i].getName().replace(".class", ""));
                //判断是否是CalPrice的实现类并且不是CalPrice它本身,满足的话加入到策略列表
                if (CalPrice.class.isAssignableFrom(clazz) && clazz != calPriceClazz) {
                    calPriceList.add((Class<? extends CalPrice>) clazz);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    //获取扫描的包下面所有的class文件
    private File[] getResources() {
        try {
            File file = new File(classLoader.getResource(CAL_PRICE_PACKAGE.replace(".", "/")).toURI());
            return file.listFiles(new FileFilter() {
                public boolean accept(File pathname) {
                    if (pathname.getName().endsWith(".class")) {//我们只扫描class文件
                        return true;
                    }
                    return false;
                }
            });
        } catch (URISyntaxException e) {
            throw new RuntimeException("未找到策略资源");
        }
    }

    public static CalPriceFactory getInstance() {
        return CalPriceFactoryInstance.instance;
    }

    private static class CalPriceFactoryInstance {

        private static CalPriceFactory instance = new CalPriceFactory();
    }
}

      2.过滤器模式

         https://blog.csdn.net/qq_37150783/article/details/79618011

      3.责任链模式

      (1)定义

               职责链模式(Chain of Responsibility):使多个对象都有机会处理同一个请求,

               从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿

               着这条链传递该请求,直到有一个对象处理它为止。

      (2)责任链模式中的角色介绍

            (a)Handler处理者

                     handler定义了处理请求的接口,handler知道,下一个处理者是谁,如果自己

                     无法处理请求,就转给下一个处理者。在实例中对应的是,support类和

                     support方法(实例在(3))。

            (b)concreteHandler(具体的处理者)

                     具体的处理者是处理请求的具体角色。在实例中,由NoSupport角色和其他

                     几个类扮演(实例在(3))。

            (c)Client

                     请求者角色,就是向第一个具体的handler发送请求的角色,并连接好责任链,

                     实例中对应的是main类的main方法(实例在(3))。

      (3)责任链模式简单的示例

               类图:

               设计模式

               Support是一个抽象类,他的核心方法support中,如果当前support可以解决,就

               解决,如果不行,就交给next去解决。

               



package ChainOfResponse;

public abstract class Support {
    private String name;
    private Support next;
    
    public Support(String name) {
        this.name = name;
    }
    
    public Support setNext(Support next) {
        this.next = next;
        return next;
    }
    
    public final void support(Trouble trouble) {
        if(resolve(trouble)) {
            done(trouble);
        } else if (next != null) {
            next.support(trouble);
        } else {
            fail(trouble);
        }
    }
    
    public String toString() {              // 显示字符串
        return "[" + name + "]";
    }
    
    protected abstract boolean resolve(Trouble trouble);
    
    protected void done(Trouble trouble) {
        System.out.println(trouble + " is resolved by " + this + ".");
    }
    
    protected void fail(Trouble trouble) {  // 未解决
        System.out.println(trouble + " cannot be resolved.");
    }
}

 

               然后我们实现几个具体的support类NoSupport类是一个永远不解决问题的类:

package ChainOfResponse;

public class NoSupport extends Support {

    public NoSupport(String name) {
        super(name);
    }

    @Override
    protected boolean resolve(Trouble trouble) {
        return false;
    }
    
}

 

               LimitSupport类,解决指定范围内的问题

LimitSupport类,解决指定范围内的问题

package ChainOfResponse;

public class LimitSupport extends Support {

    private int limit;
    
    public LimitSupport(String name, int limit) {
        super(name);
        this.limit = limit;
    }

    @Override
    protected boolean resolve(Trouble trouble) {
        if(trouble.getNumber() < limit)
            return true;
        return false;
    }
}

 

          OddSupport类,解决奇数的问题

package ChainOfResponse;

public class SpecialSupport extends Support {
    
    private int number;
    
    public SpecialSupport(String name, int number) {
        super(name);
        this.number = number;
    }

    @Override
    protected boolean resolve(Trouble trouble) {
        if(trouble.getNumber() == number)
            return true;
        return false;
    }
    
}

     

package ChainOfResponse;

public class OddSupport extends Support {

    public OddSupport(String name) {
        super(name);
    }

    @Override
    protected boolean resolve(Trouble trouble) {
        if(trouble.getNumber() % 2 == 1)
            return true;
        return false;
    }
}

 

          最后实现一个main类:

package ChainOfResponse;

public class Main {

    public static void main(String[] args) {
        
        Support alice   = new NoSupport("Alice");
        Support bob     = new LimitSupport("Bob", 100);
        Support charlie = new SpecialSupport("Charlie", 429);
        Support diana   = new LimitSupport("Diana", 200);
        Support elmo    = new OddSupport("Elmo");
        Support fred    = new LimitSupport("Fred", 300);
        
        alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred);
        
        for(int i=0;i<500;i+=33) {
            alice.support(new Trouble(i));
        }
    }

}

 

          Main类中定义了一个责任链,将几个support对象连接在一起,组成了一条责任链,

          然后去处理问题

          运行结果如下:

     设计模式

      (4)责任链模式在实际项目中的应用举例

               比如我发送的有彩信、短信、邮件,那么将如何给实体赋值?传统做法是写

               一大堆的if else,这时我们就可以使用责任链模式。

               详细例子查看:https://www.cnblogs.com/huangxincheng/p/6429284.html