设计模式之策略模式在地铁票价系统中的应用

引言

设计模式是面向对象编程的一个非常精彩的部分。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性,它能帮助我们将应用组织成容易了解,容易维护,具有弹性的架构。本文通过一个简单的案例来讲述策略模式在地铁票价系统中的应用。

案例描述

乘客从一个车站乘坐地铁到另一个车站,他/她需要购买一张票。铁路部门对于票价有一些特别的票价规定:

按照市物价主管部门批复的轨道交通网络票价体系,即:轨道交通实行按里程计价的多级票价,0~6公里3元,6公里之后每10公里增加1元;票价计算采用最短路径法,即:当两个站点之间有超过1条换乘路径时,选取里程最短的一条路径作为两站间票价计算依据。

案例分析

让我们考虑有20个站点:1s,2s,3s......20s,并且乘客在不同的场景下乘坐地铁。为了更清晰的讲述问题,我们在原有定价标准上虚拟了一些应用场景。

  • 如果乘客A乘坐的里程小于6公里,那么他将需要支付3元车票费用。

  • 如果乘客B乘坐的里程大于6公里,他将需要额外支付超出部分的车票费用,计费标准为6公里之后每10公里增加1元。

  • 如果乘客C是VIP客户,那么他将在原计费标准上享受9折优惠。

  • 如果后续有一些额外收费或额外优惠,在以上计费基础上再进行调整。

解决方案

这个问题可以通过使用“策略设计模式”来解决。因为不同类型的票价策略可以基于不同的规则来应用。 以下是票价策略的不同类型:

  • 基本票价规则战略

  • VIP票价规则策略

  • 额外的票价规则策略

每张票价规则策略将分别写入票价计算算法,这些算法不会相互干扰。 新的票价规则可以添加和写入新的票价规则策略。这种模式也将遵循“对扩展开放、对修改关闭”的理念。

依赖关系图

类图

代码说明

IFareStrategy接口

这个接口定义了票价计算的常用策略,实现一个类可以实现基于上下文的票价算法。

using TrainFair.Models;    namespace TrainFair.FareCalculator  {  public interface IFareStrategy {  float GetFare(IFareRule ruleValues, float basicFare);  }  
}  

FareConstants类
FareConstants定义了计费的规则,包括起步价,超出里程递增价及VIP折扣价。

namespace TrainFair.Constants{    
public class FareConstants {        
public const float BasicFare = 3.0F;    
   public const float OnStationFare = 1.0F;  
        public const float VIPDiscount = 0.1F;} }

StationRuleFareCalculator类

StationRuleFareCalculator类根据行驶的车站里程和问题陈述部分定义的一些规则集来计算车费。

using System;  using TrainFair.Models;  namespace TrainFair.FareCalculator  {  public class StationRuleFareCalculator : IFareStrategy  {  public float GetFare(IFareRule ruleValues, float basicFare) {  var stationFareRuleModel = ruleValues as StationFareRuleModel;  if (stationFareRuleModel == null || stationFareRuleModel.StationDistance <= 0.0f)              
 return 0;            if (stationFareRuleModel.StationDistance < 6)                return basicFare;            int restChargingStations = (int)Math.Ceiling((stationFareRuleModel.StationDistance - 6.0f)/10.0f);            var totalFare = basicFare + restChargingStations * stationFareRuleModel.IncrementalPrice;           return totalFare;}  }   }  

VIPRuleFareCalculator类
这个类实现的是VIP的票价算法。如果乘客是VIP身份,那么他/她将得到享受特殊的优惠。这个类实现了这个算法。

using TrainFair.Models;  namespace TrainFair.FareCalculator  {  public class VIPRuleFareCalculator : IFareStrategy{       
      public float GetFare(IFareRule ruleValues, float basicFare) {            var vipFareRuleModel = ruleValues as VIPFareRuleModel;  
                if (vipFareRuleModel == null)        
                       return 0;           var totalFare = basicFare - (basicFare * vipFareRuleModel.Discount);            return totalFare;}} }  

OtherRuleFareCalculator类
这个类实现的是其他额外的费用或优惠票价的算法。一些额外的价格将被添加到总费用中。额外的价格可以是附加收费(正值),也可以是额外折扣(负值)。

using TrainFair.Models;  namespace TrainFair.FareCalculator  {  
 public class OtherRuleFareCalculator : IFareStrategy{      
   public float GetFare(IFareRule ruleValues, float basicFare) {                 var otherFareRuleModel = ruleValues as OtherFareRuleModel;              if (otherFareRuleModel == null)              
      return basicFare;          
        float totalFare = basicFare + otherFareRuleModel.AdditionalFare;            return totalFare;}} }  

FareRuleCalculatorContext类

using TrainFair.Models;  namespace TrainFair.FareCalculator  {  public class FareCalculatorContext {  private readonly IFareStrategy _fareStrategy;  public FareCalculatorContext(IFareStrategy fareStrategy) {  this._fareStrategy = fareStrategy;  }  public float GetFareDetails(IFareRule fareRules, float basicFare)  {  return _fareStrategy.GetFare(fareRules, basicFare);  }  }  
}  

代码结构里有一些基于车站票价,VIP票价,额外票价等情况的model类。

IFareRule接口

这是基本票价规则模型接口,每个模型类都实现它。

namespace TrainFair.Models  {  public interface IFareRule  {  int FareRuleId { get; set; }  }  
}  

StationFareRuleModel类
这个类定义的是车站票价规则的基本属性。

namespace TrainFair.Models  {  public class StationFareRuleModel : IFareRule  {  public int FareRuleId { get; set; }   
       public int  StationsCounts { get; set; }  
       public float IncrementalPrice { get; set; }        
       public float StationDistance { get; set; }  }   }

VIPFareRuleModel类

这个类定义了VIP折扣的属性。

namespace TrainFair.Models  {  
   public class VIPFareRuleModel : IFareRule  {  public int FareRuleId { get; set; }       public float Discount { get; set; }}   }  

OtherFareRuleModel类

这个类定义其他额外收费的属性。

namespace TrainFair.Models  {  public class OtherFareRuleModel : IFareRule  {  public int FareRuleId { get; set; }  public string OtherFareName { get; set; }  public float AdditionalFare { get; set; }  }  
}  

模型的属性可以根据未来的需求进行增强和调整,并可以灵活应用在算法类中。

执行结果

以下是控制台输出:

 
本文结尾附上了程序代码。

结语

车站基础票价、VIP票价、额外票价等不同类型的票价计算规则是不同的,所有的算法都被分解到不同的类中,以便能够在运行时选择不同的算法。策略模式的用意是针对一组算法或逻辑,将每一个算法或逻辑封装到具有共同接口的独立的类中,从而使得它们之间可以相互替换。策略模式使得算法或逻辑可以在不影响到客户端的情况下发生变化。说到策略模式就不得不提及OCP(Open Closed Principle) 开闭原则,即对扩展开放,对修改关闭。策略模式的出现很好地诠释了开闭原则,有效地减少了分支语句。

程序代码:https://github.com/daivven/TrainFair

原文地址:http://www.cnblogs.com/yayazi/p/8350679.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/322246.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sentinel(十九)之主流框架的适配

转载自 主流框架的适配 注&#xff1a;适配模块仅提供相应适配功能&#xff0c;若希望接入 Sentinel 控制台&#xff0c;请务必参考 Sentinel 控制台文档。 云原生微服务体系 Spring Cloud Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。Sentinel 与 Spring …

利用OCR文字识别+百度算法搜索,玩转冲顶大会、百万英雄、芝士超人等答题赢奖金游戏

【先上一张效果图】&#xff1a;一、原理&#xff1a;其实原理很简单&#xff1a;1.手机投屏到电脑&#xff1b;2.截取投屏画面的题目部分&#xff0c;进行识别&#xff0c;得到题目和三个答案&#xff1b;3.将答案按照一定的算法&#xff0c;进行搜索&#xff0c;得出推荐答案…

Sentinel(二十)之Envoy RLS Token Server

转载自 Envoy RLS Token Server Sentinel 提供了一个 Envoy Global Rate Limiting gRPC Service 的实现 sentinel-cluster-server-envoy-rls&#xff0c;借助集群限流 token server 来为 Envoy 服务网格提供集群流量控制的能力。 Note: You can refer to here for the Englis…

g4e基础篇#5 创建分支和保存代码

使用版本控制系统最常见的工作流程就是修改代码&#xff0c;保存代码&#xff0c;共享代码。Git提供了一个简单的3步工作流&#xff0c;让你方便的完成这些操作。1. 新建工作分支2. 提交更改3. 推送分支到中心存储库与团队成员共享Git 工作流按照以上3步操作&#xff0c;我们就…

P3952-时间复杂度【模拟】

正题 题目链接:https://www.luogu.org/problemnew/show/P3952 题目大意 比较复杂就直接给截图了 解题思路 开一个栈来维护序列&#xff0c;因为它保证合法的程序中一个变量不会相互嵌套所以就拿变量的字母当做每个循环的下标即可。然后对于输入的x,yx,yx,y有五种情况 xN,yN…

一步步部署基于Windows系统的Jenkins持续集成环境

如题&#xff1a;本文将介绍如何在Windows环境下运用Jenkins部署持续集成环境。之所以写本文&#xff0c;是因为在最近工作当中&#xff0c;学习使用Jenkins时&#xff0c;确实遇到了一些问题&#xff0c;而大多数教程文档都是基于Mac或是Linux平台。为此很是头疼&#xff0c;经…

Sentinel(二十二)之使用Nacos存储规则

转载自 Spring Cloud Alibaba基础教程&#xff1a;Sentinel使用Nacos存储规则 通过上一篇《使用Sentinel实现接口限流》的介绍&#xff0c;相信大家对Sentinel已经有了初步的认识。在Spring Cloud Alibaba的整合封装之下&#xff0c;接口限流这件事情可以非常轻易的整合到我们…

vue 开发2017年变化回顾及2018年展望

vue.js 变化从 github 的发布记录我们可以看到2017年 vue.js 的第一个发布为 v2.1.9&#xff0c;最后一个为 v2.5.13&#xff0c;主要发布小版本 2.2~2.5。这些发布提升了vue 与 TypeScript 的结合、改进了对服务端渲染&#xff08;SSR&#xff09;和 native 渲染的支持、提供了…

Sentinel(二十三)之使用Apollo存储规则

转载自 Spring Cloud Alibaba基础教程&#xff1a;Sentinel使用Apollo存储规则 上一篇我们介绍了如何通过Nacos的配置功能来存储限流规则。Apollo是国内用户非常多的配置中心&#xff0c;所以&#xff0c;今天我们继续说说Spring Cloud Alibaba Sentinel中如何将流控规则存储…

Quartz.Net分布式任务管理平台

前言&#xff1a;我相信大多数人公司的业务上都有定时任务这么个功能&#xff0c;我们公司也不例外&#xff0c;刚来公司的时候使用Quartz.Net为我们组做了第一个任务&#xff0c;大致流程是&#xff1a;新建一个控制台程序&#xff0c;引用需要的程序集&#xff0c;Execute方法…

Sentinel(二十四)之Sentinel Dashboard中修改规则同步到ZooKeeper

转载自 Springboot使用Sentinel限流&#xff0c;集成zookeeper完成规则的持久化 上一篇简单介绍了sentinel限流的基本配置和使用&#xff0c;这一篇我们来稍微深入一点&#xff0c;看看如何将zookeeper继承进来&#xff0c;用以保存添加的流控规则。 上一篇中我们启动了dash…

微软Azure AspNetCore微服务实战第2期(内附PPT下载)

2018年1月28日&#xff0c;虽然上海的大雪在城区已经见不到踪影&#xff0c;但还是很冷。不过天气再冷&#xff0c;也阻止不了小伙伴参加活动的热情。感谢王振&#xff0c;苏老师以及微软Azure API Management的产品经理Alvin&#xff0c;给大家带来微服务实战&#xff0c;企业…

Sentinel(二十五)之Sentinel Dashboard同步Apollo存储规则

转载自 Spring Cloud Alibaba基础教程&#xff1a;Sentinel Dashboard同步Apollo存储规则 在之前的两篇教程中我们分别介绍了如何将Sentinel的限流规则存储到Nacos和Apollo中。同时&#xff0c;在文末的思考中&#xff0c;我都指出了这两套整合方案都存在一个不足之处&#…

为什么选择.NETCore?

为什么选择.NETCore&#xff1f;在开展话题之前先出一张ASP.NETCore VS Node.js的性能对比图 ASP.NET Core VS node.js&#xff1a;继续正文&#xff1a;学习新的开发框架是一项巨大的投资。您需要学习如何在新框架中编写&#xff0c;构建&#xff0c;测试&#xff0c;部署…

欢乐纪中某A组赛【2019.7.10】

前言 好烦我最后写对了T1T1T1的808080分结果交错题导致T2T2T2的404040分没了T1T1T1也没拿多那些分。 话说好像ZDYZDYZDY比我还惨 成绩 这里还是按OJOJOJ上的分数排名 JJJ表示初中&#xff0c;HHH表示高中后面加的是几年级 RankRankRankPersonPersonPersonScoreScoreScoreAAA…

用C#编写Linux守护进程

如果要在Red Hat Enterprise Linux上将.NET Core进程作为后台进程运行&#xff0c;则可以创建自定义systemd单元。今天我将为.NET Core编写两个自定义系统单元的例子。一个是运行.NET Core控制台应用程序的一种类型&#xff0c;另一个是运行ASP.NET Core Web应用程序的简单类型…

AWS Lambda现已支持.NET Core 2.0

Amazon宣称.NET Core 2.0现在已经支持AWS Lambda以及无服务器应用程序了。开发者们现在可以使用C#和.NET Core 2.0来为AWS Lambda编写代码和编写运行于AWS的无服务器应用程序了。自2017年2月Amazon开始了对C#编程语言的支持&#xff0c;这使得.NET开发者能够使用.NET Core 1.0运…

Nacos(一)之简介

转载自 什么是 Nacos Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集&#xff0c;帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。 Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现…

开源纯C#工控网关+组态软件(八)表达式编译器

一、 引子监控画面的主要功能之一就是跟踪下位机变量变化&#xff0c;并将这些变化展现为动画。大部分时候&#xff0c;界面上一个图元组件的某个状态&#xff0c;与单一变量Tag绑定&#xff0c;比如电机的运行态&#xff0c;绑定一个MotorRunning信号&#xff1b;但有些时候…

g4e基础篇#6 了解Git历史记录

Git的版本历史记录采用了与传统集中式版本管理系统完全不同的方式进行组织&#xff0c;在刚开始使用Git的时候我们往往会不知所措&#xff0c;比如看到这样的历史记录。看到这个七拐八拐的图形&#xff0c;你可能完全不知道它代表了什么。其实这正是Git的特别之处&#xff0c;G…