俗话说:条条大路通罗马。在很多情况下,实现某个目标的途径不止一条,例如我们在外出
旅游时可以选择多种不同的出行方式,如骑自行车、坐汽车、坐火车或者坐飞机,可根据实
际情况(目的地、旅游预算、旅游时间等)来选择一种最适合的出行方式。在制订旅行计划
时,如果目的地较远、时间不多,但不差钱,可以选择坐飞机去旅游;如果目的地虽远、但
假期长、且需控制旅游成本时可以选择坐火车或汽车;如果从健康和环保的角度考虑,而且
有足够的毅力,自行车游或者徒步旅游也是个不错的选择。
在软件开发中,我们也常常会遇到类似的情况,实现某一个功能有多条途径,每一条途径对
应一种算法,此时我们可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增
加新的解决途径。本章我们将介绍一种为了适应算法灵活性而产生的设计模式——策略模
式。
(1) MovieTicket类的calculate()方法非常庞大,它包含各种打折算法的实现代码,在代码中出现
了较长的if…else…语句,不利于测试和维护。
(2) 增加新的打折算法或者对原有打折算法进行修改时必须修改MovieTicket类的源代码,违反
了“开闭原则”,系统的灵活性和可扩展性较差。
(3) 算法的复用性差,如果在另一个系统(如商场销售管理系统)中需要重用某些打折算法,
只能通过对源代码进行复制粘贴来重用,无法单独重用其中的某个或某些算法(重用较为麻
烦)。
如何解决这三个问题?导致产生这些问题的主要原因在于MovieTicket类职责过重,它将各种
打折算法都定义在一个类中,这既不便于算法的重用,也不便于算法的扩展。因此我们需要
对MovieTicket类进行重构,将原本庞大的MovieTicket类的职责进行分解,将算法的定义和使
用分离,这就是策略模式所要解决的问题,下面将进入策略模式的学习。
策略模式
符合"依赖倒转原则"
策略模式Strategy Pattern:定义一系列算法类,
将每一个算法封装起来,并让他们可以相互替换,
策略模式让算法独立于使用它的客户而变化。
策略模式的主要目的是将算法的定义与使用分开。
// Helper method to update card data based on tag
private static void updateCardData(String tagName, byte [] data, Card card) {
// Update card data based on the tag
Logs.d(TAG,"dataOut.length: "+data.length);
String ordata = tools.hexString(data);
Logs.d(TAG,tagName + "原始数据 "+ ordata);
switch (tagName) {
case eICCardNumber:
// Skip the first two bytes, the first one is the tag, and the second is the length
byte[] tagBytes = Arrays.copyOfRange(data, 2, 2 + data[1] & 0xFF);
String tagString = tools.hexString(tagBytes);
System.out.println(tagString);
card.setPan(tagString);
break;
case epanSeqNo:
// 解析Tag
int tag = data[0] & 0xFF;
// 解析Length
int length = data[1] & 0xFF;
// 获取PAN Sequence Number的值
byte panSequenceNumber = data[2];
System.out.println("Tag: " + Integer.toHexString(tag));
System.out.println("Length: " + length);
System.out.println("PAN Sequence Number: " + panSequenceNumber);
card.setPanSeqNo((int)panSequenceNumber);
break;
case ecardHolderName:
byte[] cardHolderName = Arrays.copyOfRange(data, 3, 3 + data[2] & 0xFF);
String resultString = new String(cardHolderName, StandardCharsets.UTF_8);
System.out.println(resultString);
System.out.println("ecardHolderName: " + resultString);
card.setCardHolderName(resultString);
break;
case eserviceCode:
byte[] serviceCode = Arrays.copyOfRange(data, 3, 3 + data[2] & 0xFF);
String sserviceCode = tools.hexString(serviceCode);
card.setServiceCode(sserviceCode);
break;
case eissuerCountryCode:
byte[] issuerCountryCode = Arrays.copyOfRange(data, 3, 3 + data[2] & 0xFF);
String ssuerCountryCode = tools.hexString(issuerCountryCode);
Log.d(TAG,"ssuerCountryCode"+ ssuerCountryCode);
int CountryCode = Integer.parseInt(ssuerCountryCode, 10);
Log.d(TAG,"CountryCode"+ CountryCode);
card.setIssuerCountryCode(CountryCode);
break;
case elabel:
byte[] label = Arrays.copyOfRange(data, 2, 2 + data[1] & 0xFF);
String slabel = new String(label, StandardCharsets.UTF_8);
System.out.println(slabel);
card.setLabel(slabel);
break;
case ecardExpirationDate:
byte[] cardExpirationDate = Arrays.copyOfRange(data, 3, 3 + data[2] & 0xFF);
String scardExpirationDate = tools.hexString(cardExpirationDate);
Log.d(TAG,"scardExpirationDate"+ scardExpirationDate);
Date parsedDate = null;
SimpleDateFormat sdf = new SimpleDateFormat("MMddyy");
try {
parsedDate = sdf.parse(scardExpirationDate);
} catch (ParseException e) {
e.printStackTrace();
}
card.setCardExpirationDate(parsedDate);
break;
default:
break;
}
}
使用策略模式修改后的代码
public interface CardDataUpdateStrategy {
void updateCardData(byte[] data, Card card);
}
public class LabelStrategy implements CardDataUpdateStrategy {
@Override
public void updateCardData(byte[] data, Card card) {
byte[] label = Arrays.copyOfRange(data, 2, 2 + data[1] & 0xFF);
String slabel = new String(label, StandardCharsets.UTF_8);
System.out.println(slabel);
card.setLabel(slabel);
}
}
public class ICCData {
private static final String TAG = ICCData.class.getSimpleName();
static Card card;
static final private int expectDataLen = 20; // 期望TLV返回的长度
static final String eICCardNumber ="ICCardNumber";
static final String epanSeqNo ="panSeqNo";
static final String ecardHolderName ="cardHolderName";
static final String eserviceCode ="serviceCode";
static final String eissuerCountryCode ="issuerCountryCode";
static final String elabel ="label";
static final String ecardExpirationDate ="cardExpirationDate";
private static final Map strategyMap;
static {
strategyMap = new HashMap<>();
strategyMap.put(eICCardNumber, new PANUpdateStrategy());
strategyMap.put(epanSeqNo, new PanSeqNoUpdateStrategy());
strategyMap.put(ecardHolderName, new CardHolderNameStrategy());
strategyMap.put(eserviceCode, new ServiceCodeStrategy());
strategyMap.put(eissuerCountryCode, new IssuerCountryCodeStrategy());
strategyMap.put(elabel, new LabelStrategy());
strategyMap.put(ecardExpirationDate, new ExpirationDateStrategy());
}
// 获取 Card 实例的方法
public static Card getCardInstance() {
if (card == null) {
card = new Card();
}
return card;
}
private static void updateCardData(TagInfo tagInfo, Card card) {
String ordata = tools.hexString(tagInfo.tag);
Logs.d(TAG, tagInfo.getTagName() + "原始数据 " + ordata);
CardDataUpdateStrategy strategy = strategyMap.get(tagInfo.getTagName());
// Check if a valid strategy is found
if (strategy != null) {
strategy.updateCardData(tagInfo.getTag(), card);
}
}
public static Card getICCardNumber() {
List tagInfoList = Arrays.asList(
new TagInfo(new byte[]{(byte) 0x5A}, eICCardNumber),
new TagInfo(new byte[]{(byte) 0x5F, (byte) 0x34}, epanSeqNo),
new TagInfo(new byte[]{(byte) 0x5F, (byte) 0x20}, ecardHolderName),
new TagInfo(new byte[]{(byte) 0x5F, (byte) 0x30}, eserviceCode),
new TagInfo(new byte[]{(byte) 0x5F, (byte) 0x28}, eissuerCountryCode),
new TagInfo(new byte[]{(byte) 0x50}, elabel),
new TagInfo(new byte[]{(byte) 0x5F, (byte) 0x24}, ecardExpirationDate)
);
Card card = getCardInstance();
for (TagInfo tagInfo : tagInfoList) {
getICCardData(tagInfo, card);
}
card.setType(ICC);
return card;
}
public static Card getICCardData(TagInfo tagInfo, Card card) {
ByteArray dataOut = new ByteArray(expectDataLen);
int result = EMV_GetTLVDataList(tagInfo.tag, (byte) tagInfo.tag.length, expectDataLen, dataOut);
switch (result) {
case EMV_OK:
tagInfo.tag = Arrays.copyOfRange(dataOut.data, 0, dataOut.length);
updateCardData(tagInfo, card);
break;
case EMV_PARAM_ERR:
System.out.println("Error: Parameters error (null pointer).");
break;
case EMV_DATA_ERR:
System.out.println("Error: Data error, illegal tags.");
break;
case EMV_NO_DATA:
System.out.println("Error: The tag is not present.");
break;
case EMV_OVERFLOW:
System.out.println("Warning: Data overflow. Actual data cut off to the length of expectDataLen.");
break;
default:
// Handle other return codes as needed
break;
}
return card;
}
// 处理卡号数据
static private String processCardNumber(byte[] cardNumberData) {
// 在此处实现卡号数据的处理逻辑,例如格式化和解析
// 返回处理后的卡号字符串
return new String(cardNumberData);
}
}