• 使用工厂模式、策略模式、门面模式、单例模式、责任链模式、装饰者模式和访问者模式来实现红包雨


    红包雨是一种在移动应用程序中经常出现的营销活动,它可以在特定时间内向用户投放很多红包,来吸引用户参与活动。如何在程序中实现红包雨呢?下面将介绍如何使用设计模式来实现红包雨。

    首先,使用工厂模式来创建不同类型的红包对象。在工厂模式中,我们定义一个工厂类,该类根据输入的参数来创建不同类型的红包对象。在红包雨中,我们需要创建不同金额和不同颜色的红包,因此我们可以定义一个红包工厂类,并在该类中实现创建不同类型红包的方法。

    /* 红包工厂类,用于创建不同类型的红包对象 */
    public class RedPacketFactory {
      
      /* 根据传入的类型参数,创建不同类型的红包对象 */
      public static RedPacket createRedPacket(String type) {
        if (type.equals("fixed")) { // 如果类型为 "fixed",创建一个固定金额红包对象
          return new FixedRedPacket();
        } else if (type.equals("random")) { // 如果类型为 "random",创建一个随机金额红包对象
          return new RandomRedPacket();
        } else if (type.equals("lucky")) { // 如果类型为 "lucky",创建一个拼手气红包对象
          return new LuckyRedPacket();
        } else {
          return null; // 如果类型参数无效,则返回空对象
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在上面的代码中,我们定义了一个静态方法 createRedPacket,该方法接受一个字符串类型的参数 type,根据不同的类型参数创建不同类型的红包对象。例如,如果 type 参数为 "fixed",则创建一个固定金额的红包对象。

    调用 createRedPacket 方法来创建不同类型红包的代码:

    RedPacket fixedPacket = RedPacketFactory.createRedPacket("fixed"); // 创建一个固定金额红包对象
    RedPacket randomPacket = RedPacketFactory.createRedPacket("random"); // 创建一个随机金额红包对象
    RedPacket luckyPacket = RedPacketFactory.createRedPacket("lucky"); // 创建一个拼手气红包对象
    
    • 1
    • 2
    • 3

    上面的代码中,我们首先调用 RedPacketFactory.createRedPacket 方法来创建三个不同类型的红包对象,分别是固定金额红包、随机金额红包和拼手气红包。每个红包对象都可以调用各自的方法来设置或获取红包的金额和颜色等属性。

    接下来,我们使用策略模式来定义不同的红包金额分配算法。在策略模式中,我们定义一个接口,接口中定义了一个方法,不同的算法实现该接口。在红包雨中,我们需要根据用户的活跃度、贡献度等因素来决定红包金额的分配。我们可以定义一个 RedPacketStrategy 接口,并在该接口中定义一个方法来实现不同的红包金额分配算法。

    // 红包策略接口
    public interface RedPacketStrategy {
      // 分配红包金额
      public int allocate(int totalMoney, int totalNum, int currentNum, User user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在上面的代码中,我们定义了一个 RedPacketStrategy 接口,该接口中定义了一个方法 allocate,该方法接受四个参数:总金额、总人数、当前人数和用户对象,根据用户的活跃度、贡献度等因素来决定红包金额的分配。

    下面给出 RedPacketStrategy 接口的两个实现类,分别是普通红包算法和拼手气红包算法:

    // 普通红包算法
    public class NormalRedPacketStrategy implements RedPacketStrategy {
      @Override
      public int allocate(int totalMoney, int totalNum, int currentNum, User user) {
        int avg = totalMoney / totalNum; // 平均分配
        return avg;
      }
    }
    
    // 拼手气红包算法
    public class RandomRedPacketStrategy implements RedPacketStrategy {
      @Override
      public int allocate(int totalMoney, int totalNum, int currentNum, User user) {
        if (currentNum == totalNum) { // 最后一个人拿走剩余金额
          return totalMoney;
        }
        int leftMoney = totalMoney;
        int leftNum = totalNum - currentNum;
        int maxMoney = leftMoney / leftNum * 2;
        Random random = new Random();
        int money = random.nextInt(maxMoney) + 1;
        return money;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在应用中,我们可以根据不同的需求来选择使用不同的红包算法。下面是使用示例:

    public class RedPacketRain {
      private RedPacketStrategy strategy;
    
      public void setStrategy(RedPacketStrategy strategy) {
        this.strategy = strategy;
      }
    
      public void sendRedPacket(int totalMoney, int totalNum, List<User> userList) {
        int currentNum = 0; // 当前发出的红包数量
        for (User user : userList) {
          int money = strategy.allocate(totalMoney, totalNum, ++currentNum, user);
          // 发送红包
          sendRedPacket(user, money);
          // 减去已分配的金额
          totalMoney -= money;
        }
      }
    
      private void sendRedPacket(User user, int money) {
        // 发送红包
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在上面的示例中,我们使用了 setStrategy 方法来设置红包算法,并在 sendRedPacket 方法中调用了 allocate 方法来分配红包金额。具体实现中,我们还需要根据业务需求来对红包金额进行进一步的处理和发送。

    接下来,我们使用门面模式来启动红包雨,并隐藏红包雨系统的复杂性。在门面模式中,我们定义一个门面类,该类提供一个简单的接口来启动红包雨。在红包雨中,我们可以定义一个 RedPacketRain 类,并在该类中实现一个启动红包雨的方法。

    public class RedPacketRain {
      public void start(int duration) {
        // 启动红包雨系统
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在上面的代码中,我们定义了一个 start 方法,该方法接受一个参数 duration,该参数表示红包雨的持续时间。在该方法中,我们可以调用其他类的方法来启动红包雨系统。

    以下是使用门面模式启动红包雨的代码:

    // RedPacketRainFacade类
    public class RedPacketRainFacade {
      // 定义私有的红包雨对象
      private RedPacketRain redPacketRain;
    
      // 构造函数初始化红包雨对象
      public RedPacketRainFacade() {
        this.redPacketRain = new RedPacketRain();
      }
    
      // 开始红包雨,参数为持续时间
      public void startRedPacketRain(int duration) {
        redPacketRain.start(duration);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在以上代码中,我们定义了一个门面类 RedPacketRainFacade,该类实例化了一个 RedPacketRain 对象,并提供了一个名为 startRedPacketRain 的方法,该方法接受一个 duration 参数表示红包雨的持续时间。在 startRedPacketRain 方法中,我们调用了 RedPacketRain 类的 start 方法来启动红包雨系统。

    以下是使用门面模式启动红包雨的调用方法代码:

    public static void main(String[] args) {
      RedPacketRainFacade facade = new RedPacketRainFacade();
      facade.startRedPacketRain(60); // 启动红包雨,持续60秒
    }
    
    • 1
    • 2
    • 3
    • 4

    在以上代码中,我们实例化了 RedPacketRainFacade 类,并调用了其 startRedPacketRain 方法来启动红包雨系统,持续时间为60秒。由于门面模式将红包雨系统的复杂性隐藏在门面类中,因此调用方只需要使用简单的接口就可以启动红包雨系统了。

    接下来,我们使用单例模式来保证全局唯一性。在单例模式中,我们定义一个类,该类只允许创建一个实例,并且提供一个全局访问该实例的方法。在红包雨中,我们需要保证红包雨系统只有一个实例,因此我们可以定义一个 RedPacketRainSystem 类,并在该类中实现单例模式。

    /**
     * 红包雨系统,实现单例模式
     */
    public class RedPacketRainSystem {
      
      // 声明一个静态的实例变量
      private static RedPacketRainSystem instance;
    
      // 构造函数私有化,禁止外部创建实例
      private RedPacketRainSystem() {}
    
      // 对外提供获取单例的静态方法
      public static synchronized RedPacketRainSystem getInstance() {
        // 如果实例变量为空,则创建一个新实例
        if (instance == null) {
          instance = new RedPacketRainSystem();
        }
        // 返回实例变量
        return instance;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在上面的代码中,我们定义了一个 RedPacketRainSystem 类,并将其构造方法定义为私有的,这样就只能从该类内部创建实例。同时,我们定义了一个静态方法 getInstance,该方法返回该类的唯一实例。

    调用 RedPacketRainSystem 的单例实例可以直接使用 getInstance() 静态方法,如下:

    RedPacketRainSystem system = RedPacketRainSystem.getInstance();
    // 使用 system 对象进行红包雨系统的相关操作
    
    • 1
    • 2

    其中,system 变量即为 RedPacketRainSystem 的单例实例。在代码中,只需调用该静态方法即可获取该实例,无需再使用 new 关键字创建对象。

    接下来,我们使用责任链模式来处理红包分配的请求。在责任链模式中,我们定义一个处理者抽象类,实现一个 handleRequest 方法来处理请求,同时定义一个 setNext 方法,用来设置下一个处理者。在红包雨中,我们需要按照一定规则对红包进行分配,因此我们可以定义一个 RedPacketHandler 抽象类,并在该类中实现责任链模式。

    /**
     * 红包处理器抽象类
     */
    public abstract class RedPacketHandler {
       protected RedPacketHandler nextHandler; //下一个处理器
    
       /**
        * 设置下一个处理器
        * @param handler 处理器对象
        */
       public void setNext(RedPacketHandler handler) {
          this.nextHandler = handler;
       }
    
       /**
        * 抽象方法,处理红包请求
        * @param user 用户对象
        * @param redPacket 红包对象
        */
       public abstract void handleRequest(User user, RedPacket redPacket);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在上面的代码中,我们定义了一个 RedPacketHandler 抽象类,并定义了一个 setNext 方法用于设置下一个处理者,实现了责任链模式。

    接下来我们可以在 RedPacketHandler 类的子类中实现具体的红包分配规则。例如,我们可以定义一个按照红包金额大小顺序分配红包的处理器 AmountHandler

    /**
     * 按照红包金额大小顺序分配红包的处理器
     */
    public class AmountHandler extends RedPacketHandler {
       @Override
       public void handleRequest(User user, RedPacket redPacket) {
          List<Double> amounts = redPacket.getAmounts();
          Collections.sort(amounts); //按照金额大小排序
          for (Double amount : amounts) {
             if (user.getBalance() >= amount) { //如果用户余额足够
                user.withdraw(amount); //从用户余额中扣除红包金额
                redPacket.addRecord(user.getUserId(), amount); //记录红包分配记录
             } else {
                break; //如果用户余额不足,则停止分配红包
             }
          }
          if (nextHandler != null) {
             nextHandler.handleRequest(user, redPacket); //交给下一个处理器处理
          }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在上面的代码中,我们重写了 RedPacketHandler 中的 handleRequest 方法,实现了按照红包金额大小顺序分配红包的逻辑,如果用户余额足够,则从用户余额中扣除红包金额,同时记录红包分配记录。如果用户余额不足,则停止分配红包。最后,如果有下一个处理器,则将请求交给下一个处理器处理。

    我们还可以定义其他的处理器,如按照领取时间顺序分配红包的处理器 TimeHandler,按照随机顺序分配红包的处理器 RandomHandler 等。

    接下来,我们可以在调用代码中创建一个红包对象和多个用户对象,并将红包分配请求交给责任链处理器处理:

    public static void main(String[] args) {
       RedPacket redPacket = new RedPacket(100.0, 5); //创建一个红包对象,总金额100元,共分5个红包
       List<User> userList = new ArrayList<>(); //创建多个用户对象
       for (int i = 1; i <= 10; i++) {
          User user = new User("user" + i, i * 10.0); //每个用户初始余额为10元
          userList.add(user);
       }
    
       //创建责任链处理器
       RedPacketHandler amountHandler = new AmountHandler();
       RedPacketHandler timeHandler = new TimeHandler();
       RedPacketHandler randomHandler = new RandomHandler();
       amountHandler.setNext(timeHandler);
       timeHandler.setNext(randomHandler);
    
       //将红包分配请求交给责任链处理器处理
       for (User user : userList) {
          amountHandler.handleRequest(user, redPacket);
       }
    
       //输出红包分配记录
       List<RedPacketRecord> recordList = redPacket.getRecordList();
       for (RedPacketRecord record : recordList) {
          System.out.println(record.getUserId() + "领取了" + record.getAmount() + "元");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在上面的代码中,我们创建了一个红包对象和多个用户对象,并创建了责任链处理器 amountHandlertimeHandlerrandomHandler,将红包分配请求按照指定顺序交给处理器处理。最后输出红包分配记录。

    接下来,我们使用装饰者模式动态地添加额外的红包属性,如红包颜色、红包大小等。在装饰者模式中,我们定义一个装饰器抽象类,实现一个 decorate 方法来装饰对象,同时定义一个 setComponent 方法,用来设置被装饰对象。在红包雨中,我们需要对红包进行装饰,如添加红包颜色、红包大小等属性,因此我们可以定义一个 RedPacketDecorator 抽象类,并在该类中实现装饰者模式。

    /**
     * 红包装饰器抽象类,继承自红包类
     */
    public abstract class RedPacketDecorator extends RedPacket {
      protected RedPacket redPacket;
    
      /**
       * 构造方法,接收一个红包对象作为参数
       * @param redPacket 红包对象
       */
      public RedPacketDecorator(RedPacket redPacket) {
        this.redPacket = redPacket;
      }
    
      /**
       * 装饰方法,由具体装饰器实现
       */
      public abstract void decorate();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在上面的代码中,我们定义了一个 RedPacketDecorator 抽象类,并定义了一个 decorate 方法用于装饰对象,实现了装饰者模式。

    下面是一个添加红包颜色和大小的装饰器 ColorAndSizeRedPacketDecorator 的具体实现:

    /**
     * 添加颜色和大小装饰器
     */
    public class ColorAndSizeRedPacketDecorator extends RedPacketDecorator {
    
      public ColorAndSizeRedPacketDecorator(RedPacket redPacket) {
        super(redPacket);
      }
    
      @Override
      public void decorate() {
        // 添加颜色
        String color = generateRandomColor();
        redPacket.setColor(color);
    
        // 添加大小
        int size = generateRandomSize();
        redPacket.setSize(size);
      }
    
      private String generateRandomColor() {
        // 生成随机颜色代码
      }
    
      private int generateRandomSize() {
        // 生成随机大小代码
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    使用装饰器模式来装饰红包对象,我们可以这样调用:

    // 创建一个红包对象
    RedPacket redPacket = new RedPacket();
    
    // 使用装饰器添加颜色和大小
    RedPacket decoratedRedPacket = new ColorAndSizeRedPacketDecorator(redPacket);
    decoratedRedPacket.decorate();
    
    // 使用装饰器添加其他属性
    decoratedRedPacket = new OtherRedPacketDecorator(decoratedRedPacket);
    decoratedRedPacket.decorate();
    
    // ... 继续添加其他装饰器 ...
    
    // 将装饰后的红包对象加入红包雨中
    redPacketRain.add(decoratedRedPacket);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    最后,我们使用访问者模式为红包添加新的访问者,如统计红包数量、红包金额等。在访问者模式中,我们定义一个访问者接口,接口中定义了访问红包对象的方法,不同的访问者实现该接口。在红包雨中,我们需要对红包进行统计,例如统计红包数量、红包金额等,因此我们可以定义一个 RedPacketVisitor 接口,并在该接口中定义一个方法来统计红包信息。

    // 定义了一个接口 RedPacketVisitor,用于访问红包
    public interface RedPacketVisitor {
        
        // 用于访问红包的方法 visit
        void visit(RedPacket redPacket);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在上面的代码中,我们定义了一个 RedPacketVisitor 接口,并定义了一个 visit 方法用于访问红包对象。

    接下来,我们可以分别实现不同的访问者,例如统计红包数量的 CountVisitor 和统计红包金额的 AmountVisitor

    // 红包访问者类-统计红包数量
    public class CountVisitor implements RedPacketVisitor {
        private int count = 0; // 计数器
    
        @Override
        public void visit(RedPacket redPacket) {
            count++; // 访问红包,计数器加1
        }
    
        public int getCount() {
            return count; // 获取红包数量
        }
    }
    
    // 红包访问者类-统计红包金额
    public class AmountVisitor implements RedPacketVisitor {
        private int amount = 0; // 金额统计器
    
        @Override
        public void visit(RedPacket redPacket) {
            amount += redPacket.getAmount(); // 访问红包,金额统计器累加红包金额
        }
    
        public int getAmount() {
            return amount; // 获取红包金额总数
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在上面的代码中,我们分别定义了 CountVisitorAmountVisitor 类,并实现了 RedPacketVisitor 接口中的 visit 方法,分别用于统计红包数量和红包金额。

    接着,我们可以编写测试代码,创建一些红包对象,并使用不同的访问者来统计红包信息:

    // 创建一个名为Test的类
    public class Test {
        // 在main方法中执行程序
        public static void main(String[] args) {
            // 创建三个红包对象,分别为10元、20元、30元
            RedPacket redPacket1 = new RedPacket(10);
            RedPacket redPacket2 = new RedPacket(20);
            RedPacket redPacket3 = new RedPacket(30);
    
            // 创建一个红包数量统计访问者
            CountVisitor countVisitor = new CountVisitor();
            // 访问第1个红包,并统计红包数量
            redPacket1.accept(countVisitor);
            // 访问第2个红包,并统计红包数量
            redPacket2.accept(countVisitor);
            // 访问第3个红包,并统计红包数量
            redPacket3.accept(countVisitor);
            // 输出红包数量
            System.out.println("红包数量:" + countVisitor.getCount());
    
            // 创建一个红包金额统计访问者
            AmountVisitor amountVisitor = new AmountVisitor();
            // 访问第1个红包,并统计红包金额
            redPacket1.accept(amountVisitor);
            // 访问第2个红包,并统计红包金额
            redPacket2.accept(amountVisitor);
            // 访问第3个红包,并统计红包金额
            redPacket3.accept(amountVisitor);
            // 输出红包金额
            System.out.println("红包金额:" + amountVisitor.getAmount());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在上面的代码中,我们创建三个红包对象 redPacket1redPacket2redPacket3,分别设置不同的金额,并创建了 CountVisitorAmountVisitor 两个访问者。

    接着,我们使用访问者访问不同的红包对象,从而统计红包数量和红包金额,并输出统计结果。

    输出结果为:

    红包数量:3
    红包金额:60
    
    • 1
    • 2

    说明统计功能已经成功实现。

    综上所述,我们使用工厂模式、策略模式、门面模式、单例模式、责任链模式、装饰者模式和访问者模式来实现红包雨。工厂模式根据类型来创建不同的红包对象;策略模式定义不同的红包金额分配算法,根据用户的活跃度、贡献度等因素来决
    定红包金额的分配;门面模式启动红包雨,隐藏红包雨系统的复杂性;单例模式保证全局唯一性;责任链模式处理红包分配的
    请求,每个处理者都有机会处理请求;装饰者模式动态地添加额外的红包属性,如红包颜色、红包大小等;访问者模式为红包
    添加新的访问者,如统计红包数量、红包金额等。

  • 相关阅读:
    拉格朗日乘子法
    Vue中引入一个异步组件
    软考高项-项目文档管理
    Liunx常用命令
    【Redis-09】面试题之Redis数据结构与对象-RedisObject(下篇)
    前后端分离计算机毕设项目之基于SpringBoot的无人智慧超市管理系统的设计与实现《内含源码+文档+部署教程》
    软件工程理论与实践 (吕云翔) 第四章 结构化分析课后习题及答案
    ELF和静态链接:为什么程序无法同时在Linux和Windows下运行?
    【uniapp小程序】覆盖图片容器cover-image
    基础算法:二分查找、异或运算
  • 原文地址:https://blog.csdn.net/java_wxid/article/details/132761875