网站备案花钱么,中国优秀网页设计案例,wordpress展示产品,青岛做网站公司哪家好最近有个三端统一的技术场景#xff0c;主要是以前移动端的 hybrid 网页在不考虑 UI 适配的情况下、期望能够直接在 PC 客户端投放。在评估修改面的时候发现了一段可以深思的代码#xff1a;
if (platform iphone) {location.href iphoneClientUrl;
} else {location.href…最近有个三端统一的技术场景主要是以前移动端的 hybrid 网页在不考虑 UI 适配的情况下、期望能够直接在 PC 客户端投放。在评估修改面的时候发现了一段可以深思的代码
if (platform iphone) {location.href iphoneClientUrl;
} else {location.href gphoneClientUrl;
}其中platform是来自平台判断函数获得的当前系统标识、其值如iphoneiPhone、gphone安卓iphoneClientUrl/gphoneClientUrl分别是 iPhone 和安卓应用的 URL Schemes 客户端协议跳转地址。
我们知道根据不同系统/应用进行区分处理是常有的事、比如这里的调用不同协议那么这段代码在当前面临适配 PC 运行的场景会有什么样的问题呢
问题 1.不合理的兜底处理
首先如果直接在 PC 客户端投放的话这段代码会直接走进else的执行分支、即会调用安卓的客户端协议跳转地址gphoneClientUrl。这种情况大概率是调不通的、会容易导致执行异常比如跳到空白页之类。
所以这段代码的第一个问题就是不能让安卓逻辑的执行代码作为最后 else 的兜底PC 端运行安卓 mobile 的代码容易出错。 为了修改这个问题、之前的代码可以改为
if (platform iphone) {location.href iphoneClientUrl;
} else if (platform gphone) {location.href gphoneClientUrl;
} else {// 兜底处理console.log(当前系统未支持此协议调用);
}这里增加了一个未识别平台的兜底处理避免直接运行 mobile 端的兜底处理。
*这类兜底判断做法我们可以在很多大厂的代码中发现如下是百度的一段
问题 2.没有较好得遵循“开闭原则”
为了适配当前 PC 客户端的需求这段代码现在还要对 PC 客户端的协议进行判断处理如
if (platform iphone) {location.href iphoneClientUrl;
} else if (platform gphone) {location.href gphoneClientUrl;
} else if (platform windows) {location.href windowsClientUrl;
} else {// 兜底处理console.log(当前系统未支持此协议调用);
}那么问题可能又来了如果要适配 Mac、iPad、Linux 甚至鸿蒙等系统这段代码又要进行调整如
if (platform iphone) {location.href iphoneClientUrl;
} else if (platform gphone) {location.href gphoneClientUrl;
} else if (platform windows) {location.href windowsClientUrl;
} else if (platform mac) {location.href macClientUrl;
} else if (platform ipad) {location.href ipadClientUrl;
} else if (platform linux) {location.href linuxClientUrl;
} else if (platform harmony) {location.href harmonyClientUrl;
} else {// 兜底处理console.log(当前系统未支持此协议调用);
}也就是说每当要适配一个新的系统就需要再增加一条 else 判断那么这段代码就没有较好得遵循“开闭原则”、不易维护。因为这段代码的主体逻辑是根据不同平台进行协议跳转而我们的改动只是增加一个新的平台处理、不应该对主体代码进行修改。 另外这样的代码也使得这段的代码重点迷失从原本的关注根据 url 进行跳转变成了关注通过各分支进行跳转处理。
那么这段代码应该如何调整呢先放调整后的参考代码
const PLATFORM_CLIENT_URLS {iphone: iphoneClientUrl,gphone: gphoneClientUrl,windows: windowsClientUrl,mac: macClientUrl,ipad: ipadClientUrl,linux: linuxClientUrl,harmony: harmonyClientUrl,
};// 调用体
function jumpToClientUrl(platform) {const clientUrl PLATFORM_CLIENT_URLS[platform];if (clientUrl) {location.href clientUrl;} else {// 兜底处理console.log(当前系统未支持此协议调用);}
}这里我们用对象字面量PLATFORM_CLIENT_URLS来收口各系统及其对应协议地址抽象了根据不同平台进行协议跳转的主体逻辑至jumpToClientUrl方法中这样做的好处是每当要适配或调整一个新的系统时我们只需要修改PLATFORM_CLIENT_URLS即可这个对象还可以放在配置文件中与运行时代码解耦从而使调整时甚至不用改动运行时代码。
当然这种情况下比较简单那么遇到稍复杂些的场景应该怎么样呢
场景 1.各判断分支的判断条件或对应执行处理都不一样时。
继续延续前面代码的场景首先看判断条件不一样的情况比如假设
iPhone 需要大于 iOS10osVersion 10安卓需要在安卓 6 10 区间osVersion 6 osVersion 8windows 必须是 Windows 8.1 版本osVersion 8.1
这种情况下刚才的对象字面量方式就不能进行直接处理了那么应该如何适配呢
抽离判断分支对刚才的对象字面量进行调整
const PLATFORM_CLIENT_SCHEMA {iphone: {rule: osVersion osVersion 10,url: iphoneClientUrl,},gphone: {rule: osVersion osVersion 6 osVersion 8,url: gphoneClientUrl,},windows: {rule: osVersion osVersion 8.1,url: windowsClientUrl,},mac: {rule: osVersion osVersion 0,url: macClientUrl,},
};// 调用体
function jumpToClientUrl(platform, osVersion) {const clientSchema PLATFORM_CLIENT_SCHEMA[platform];let jumpClientUrl ;// 如果有规则且判断通过if (clientSchema?.rule?.(osVersion)) {jumpClientUrl clientSchema.url;}if (jumpClientUrl) {location.href jumpClientUrl;} else {// 兜底处理console.log(当前系统未支持此协议调用);}
}我们对需要单独进行判断的系统场景进行了结构调整将特殊判断用rule字段抽离同样保持了适配一个新系统只需要调整对象PLATFORM_CLIENT_RULES_AND_URLS而不用修改jumpToClientUrl函数。
再看执行不一致的场景假如
iPhone 是打开一个弹窗Alert.show()安卓是调用 js 方法而不是跳转callAndroidNative(gphoneClientUrl)windows 是window.open()打开协议地址window.open(windowsClientUrl)
这种情况下可以延续刚才判断条件的抽离、进行
抽离执行语句对刚才的对象字面量进行调整
const PLATFORM_CLIENT_SCHEMA {iphone: {rule: osVersion osVersion 10,url: iphoneClientUrl,run: () Alert.show(),},gphone: {rule: osVersion osVersion 6 osVersion 8,url: gphoneClientUrl,run: () callAndroidNative(gphoneClientUrl),},windows: {rule: osVersion osVersion 8.1,url: windowsClientUrl,run: () window.open(windowsClientUrl),},mac: {rule: osVersion osVersion 0,url: macClientUrl,},
};// 调用体
function jumpToClientUrl(platform, osVersion) {const clientSchema PLATFORM_CLIENT_SCHEMA[platform];let jumpClientUrl ;// 如果有规则且判断通过if (clientSchema?.rule?.(osVersion)) {// 如果有单独执行条件if (clientSchema.run) {return clientSchema.run();}jumpClientUrl clientSchema.url;}if (jumpClientUrl) {location.href jumpClientUrl;} else {// 兜底处理console.log(当前系统未支持此协议调用);}
}进一步对需要单独执行处理的系统场景进行了结构调整将特殊处理用run字段抽离同样保持了适配一个新系统只需要调整对象PLATFORM_CLIENT_SCHEMA而不用修改jumpToClientUrl函数。
场景 2.考虑拓展应用场景。刚才我们所做的一系列优化实质还只是在一个小应用场景如何将类似的系统判断处理通用化呢
我们可以定义抽象类或接口、将各系统的属性信息、各类判断和执行方法作为此抽象类的实现类中将各场景的消费处理放到消费类中。然后通过类似策略模式、模版模式甚至适配器模式供消费类使用。可以通过如策略模式来实现判断条件和执行逻辑的统一抽象提高整体代码的可扩展性、复用性和可读性。
那么接下来就以策略模式为例实现一个简单的跨端 api 封装因为 js 中还没有抽象类/接口语法下面就用 ts 来实现代码效果
策略模式 策略模式作为一种软件设计模式指对象有某个行为但是在不同的场景中该行为有不同的实现算法。比如每个人都要“交个人所得税”但是“在美国交个人所得税”和“在中华民国交个人所得税”就有不同的算税方法。——WikiPedia-策略模式 先来回顾下策略模式的概念在策略模式Strategy Pattern中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式中我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。策略模式就是能够把一系列“可互换的”算法封装起来并根据用户需求来选择其中一种。 策略模式实现的核心就是将算法的使用和算法的实现分离。算法的实现交给策略类。算法的使用交给环境类环境类会根据不同的情况选择合适的算法。
UML 如
优点 算法可以自由切换。避免使用多重条件判断。扩展性良好。 缺点 策略类会增多。所有策略类都需要对外暴露。
策略模式非常适合我们之前系统环境判断的处理以下是一个实现 demo
策略模式实现系统判断及处理
接口
interface PlatformStrategy {// 跳转场景jumpClient(): void;// 其他场景、如设置标题setTitle(): void;
}实现类策略类
class IphoneStrategy implements PlatformStrategy {jumpClient(osVersion: number) {if (osVersion 10) {Alert.show();} else {console.log(当前系统未支持此协议调用iOS版本小于10);}}setTitle(title: string) {document.title ${title}(iPhone);}
}class GphoneStrategy implements PlatformStrategy {jumpClient(osVersion: number) {if (osVersion 6 osVersion 8) {callAndroidNative(gphoneClientUrl);} else {console.log(当前系统未支持此协议调用安卓版本小于6或大于8);}}setTitle(title: string) {setAndroidTitle(title);}
}class OtherStrategy implements PlatformStrategy {jumpClient(osVersion: number) {console.log(当前系统未支持此协议调用);}setTitle(title: string) {console.log(当前系统未支持此协议调用);}
}消费类环境类
class PlatformCustom {platformStrategy: PlatformStrategy;constructor(platformStrategy: PlatformStrategy) {this.platformStrategy platformStrategy;}setHomePageTitle() {this.platformStrategy.setTitle(主页);}setHomeRuleTitle() {this.platformStrategy.setTitle(规则页);}jumpClient() {this.platformStrategy.jumpClient(osVersion);}
}使用
const NowPlatformStrategy STRATEGY_MAP[platform] || OtherStrategy;
const platformCustomer new PlatformCustom(new NowPlatformStrategy());// ...
platformCustomer.setHomePageTitle();// ...
platformCustomer.jumpClient();可以发现我们在消费类的定义和使用时无须关系各系统环境的处理、进行在面临适配新系统时也不用对使用或消费类进行修改很好得遵循了“开闭原则”。
另外在大前端领域下这类模式也适合跨端 Api 的封装大家可以看各类跨端框架如 Taro的封装、都或多或少遵循了策略模式/适配器模式。
总结
本文的优化建议
从本次前端系统区分判断处理的业务场景以及一段代码的优化处理下本次提出的前端优化建议有以下几点
我们需要合理设计兜底处理避免在适配新场景下直接调用不兼容的代码涉及较多判断的场景下我们可以使用抽象方式进行处理、遵循开闭原则策略模式/适配器模式/模版模式可以应用于一些统一处理的场景、比如跨端统一判断逻辑我们需要持续学习设计模式、思考在前端的实践应用
*可能伴随的问题
上述的各类对 if 处理做了各种抽象这种情况有没有什么问题隐患呢
如果硬要说隐患的话有以下两点几乎可以不值一提的隐患
多创建了枚举/对象/类占用了空间。代码的理解成本或许有所增高、没有直接 if else 看得顺畅。
思考
我们还有哪些场景可以提前做 if 语句的抽象优化比如你需要处理各家银行、各个城市、各类水果、各只基金代码等等… 在处理这些场景时我们是否需要提前引入设计模式如果需要、判断条件会是什么 推荐阅读
《重构-改善既有代码的设计》《代码整洁之道》《编程珠玑》《程序员的思维修炼:开发认知潜能的九堂课》
以上这些经典书籍都包含了 if 语句优化方面的内容。