策略模式——strategy

Strategy Pattern

       本文总结《Design Pattern in Java Chap23 Strategy.   以为用户推荐一款烟火制品选择采取不同推荐策略为例。先讲解如果不采用策略模式会怎样,然后将这些代码重构为策略模式。

 

1 、如果不使用策略模式 Customer::getRecommended() 中封装了复杂的策略选择和策略执行流程。 见图一

 
策略模式——strategy

关键代码(choice & execution of various strategies)

    public Firework getRecommended() {

        //1 、如果有促销的烟火制品,从 config/strategy.dat 中取出并返回

        try {

            Properties p = new Properties();

            p.load(ClassLoader.getSystemResourceAsStream ( "config/strategy.dat" ));

            String promotedName = p.getProperty( "promote" );

            if (promotedName != null ) {

                Firework f = Firework.lookup (promotedName);

                if (f != null )

                    return f;

            }

        } catch (Exception ignored) {

            // If resource missing or it failed to load,

            // fall through to the next approach.

            ignored.printStackTrace();

        }

 

        //2 、注册用户使用 Rel8 引擎

        if (isRegistered()) {

            return (Firework) Rel8.advise ( this );

        }

 

        //3 、未注册用户,若是大客户,使用 LikeMyStuff 引擎

        if (isBigSpender()) {

            return (Firework) LikeMyStuff.suggest ( this );

        }

 

        //4 、未注册用户,若是小客户,随意推荐

        return Firework.getRandom ();

    }

 

由以上代码可知,主要有这样几种推荐策略:

         A. config/strategy.dat 中读取指定推荐的烟火制品

         B.Rel8 引擎

         C.LikeMyStuff 引擎

         D. 随机推荐

 

 

2 、使用策略模式 ,对以上代码进行重构

 
策略模式——strategy

       现在,Customer 中的关键代码是:

public static void main(String[] args) {

        Firework recommendation = new Customer2().getRecommended();

        System. out .println(recommendation.toString());

    }

    public Firework getRecommended() {

        Firework recommend = getAdvisor().recommend( this );

        return recommend;

}

    private Advisor getAdvisor() {

        if ( advisor == null ) {

            if ( promotionAdvisor .hasItem())

                advisor = promotionAdvisor ;

            else if (isRegistered())

                advisor = groupAdvisor ;

            else if (isBigSpender())

                advisor = itemAdvisor ;

            else

                advisor = randomAdvisor ;

        }

        return advisor ;

}

可见,我们将策略选择流程封装在getAdvisor() 中了,getAdvisor() 返回一个策略接口Advisor 的子类实例;调用这个被返回的子类实例的recommend() 方法(重写Advisor 中的recommend() 方法),可以得到推荐的Firework 实例。

 

*****************************************************************

*****************************************************************

另外,对于以前写得很松散的以下两种推荐方式,分别写Advisor 子类对其进行封装。封装前如下:

public Firework getRecommended() {

        //1 、如果有促销的烟火制品,从 config/strategy.dat 中取出并返回

        try {

            Properties p = new Properties();

            p.load(ClassLoader.getSystemResourceAsStream ( "config/strategy.dat" ));

            String promotedName = p.getProperty( "promote" );

            if (promotedName != null ) {

                Firework f = Firework.lookup (promotedName);

                if (f != null )

                    return f;

            }

        } catch (Exception ignored) {

            // If resource missing or it failed to load,

            // fall through to the next approach.

            ignored.printStackTrace();

        }

 

        //2 、注册用户使用 Rel8 引擎

 

        //3 、未注册用户,若是大客户,使用 LikeMyStuff 引擎

 

        //4 、未注册用户,若是小客户,随意推荐

        return Firework.getRandom ();

    }

封装后如下:

public class PromotionAdvisor implements Advisor {

    private Firework promoted ;

 

    public PromotionAdvisor() {

        try {

            Properties p = new Properties();           

           p.load(ClassLoader.

              getSystemResourceAsStream ( "config/strategy.dat" ));

            String promotedFireworkName = p.getProperty( "promote" );

            if (promotedFireworkName != null )

                promoted = Firework.lookup (promotedFireworkName);

        } catch (Exception ignored) {

            promoted = null ;

        }

}

    public boolean hasItem() {

        return promoted != null ;

    }

    public Firework recommend(Customer c) {

        return promoted ;

}

}

 

public class RandomAdvisor implements Advisor {

    public Firework recommend(Customer c) {

        return Firework.getRandom ();

    }

}

 

3 、总结

 

    如何记忆策略模式呢:

           (1) 这里有一个概念(策略选择==> customer2.getAdvisor() )、两个实体(策略调用者==> customer2 ; 策略==> advisor接口实例 ):

           (2) 具体细节 是: “策略调用者”调用其“策略选择”customer2.getAdvisor()选择一个具体的advisor接口实例(如groupAdvisor实例)赋予其成员对象advisor;advisor接口提供统一接口方法advisor.recommend()供实际执行某一个“策略”。

策略模式——strategy