前端开发设计模式:工厂模式(Factory Pattern)【下】
承接上文:《前端开发设计模式:工厂模式(Factory Pattern)【上 】》
三、常用场景
简单工厂模式
场景特点:适用于创建对象逻辑简单,且产品类型相对较且需求相对稳定,不经常添加新的产品类型的场景。
具体应用:
例1:创建不同类型的 表单/按钮 组件,组件类型固定且后续不太可能有大量新类型添加
<!-- text-input.vue 子组件 应该存在多个 --> <template> <input type="text" /> </template> <script setup lang="ts" name="TextInput"></script>
// factories.js --- 创建工厂函数
import TextInput from '@/components/text-input.vue'
import SelectInput from '@/components/select-input.vue'
const componentFactory = {
createComponent(type){
const components = {
'A': TextInput,
'B': SelectInput
// 可扩展其他组件
}
if(!components[type]){
console.error(`未找到类型为${type}的组件`)
return null // 或返回默认组件
}
return components[type]
}
}
export default componentFactory
<!-- parent.vue --- 在组件中使用工厂模式 -->
<template>
<div>
<!-- 动态组件渲染 -->
<component :is="currentComponent" v-if="currentComponent"></component>
</div>
</template>
<script setup lang="ts">
import componentFactory from "./factories.js";
const props = defineProps({
type: {
type: String,
default: "",
},
});
// 工厂函数动态获取组件
const currentComponent = computed(() => {
return componentFactory.createComponent(props.type);
});
</script>
从这个示例中可以明确看出工厂模式的优点就是将创建与使用分离。
例2:根据用户的权限级别创建不同的导航菜单组件。
// menu-factory.js // 定义基础导航菜单类 class BaseNavMenu { constructor() { this.lists = []; } render() { return this.lists; } } // 定义普通用户导航菜单类 class NormalUserNavMenu extends BaseNavMenu { constructor() { super(); this.lists = [ { name: "首页", path: "/", }, { name: "关于我们", path: "/about", }, { name: "联系我们", path: "/contact", }, ]; } } // 定义管理员用户导航菜单类 class AdminUserNavMenu extends BaseNavMenu { constructor() { super(); this.lists = [ { name: "首页", path: "/", }, { name: "用户管理", path: "/users", }, { name: "文章管理", path: "/articles", }, { name: "关于我们", }, ]; } } // 定义导航菜单工厂函数 function NavMenuFactory(role){ switch (role) { case 1: return new NormalUserNavMenu(); case 0: return new AdminUserNavMenu(); default: throw new Error('Invalid role'); } }
<!-- menu.vue --> <template> <div class="menu-container"> <div v-for="item in menuList" :key="item.name" @click.stop="toPage(item.path)" > {{ item.name }} </div> </div> </template> <script lang="ts" setup> import { ref } from "vue"; import menuFactory from "./menu-factory.js"; const props = defineProps({ userLevel: { type: Number, required: true, }, }); const menuList = ref([]); menuList.value = menuFactory(props.userLevel).render(); </script>
例3:游戏中创建不同的游戏角色。--- 代码差不多就不实现了。
工厂方法模式
将对象的创建抽成一个抽象方法,由具体子类实现这个方法,符合开闭原则。
场景特点:适用于创建对象的逻辑较为复杂,且产品类型较多,需频繁扩展的场景
具体应用:
例1:电商系统中,商品类型不断增加,比如有普通商品,促销商品,限时抢购商品等,每种商品的创建逻辑不同。
// factories.js import EmptyComponent from "./empty-component.vue"; import NormalComponent from "./normal-component.vue"; import PromotionComponent from "./promotion-component.vue"; class Product { showComponents() { console.log("该方法必须被重写"); return EmptyComponent; } } // 产品子类 class NormalProduct extends Product { showComponents() { return NormalComponent; } } class promotionProduct extends Product { showComponents() { return PromotionComponent; } } class ProductFactory { createProduct() { throw new Error("抽象类不能实例化,该方法必须重写"); } } class NormalProductFactory extends ProductFactory { createProduct() { return new NormalProduct(); } } class PromotionProductFactory extends ProductFactory { createProduct() { return new promotionProduct(); } } export { NormalProductFactory, PromotionProductFactory };
<!-- detail.vue --> <template> <div class="detail-container"> <component :is="currentComponents"></component> </div> </template> <script lang="ts" setup> import { NormalProductFactory, PromotionProductFactory } from "./factories.js"; const props = defineProps({ detail: { type: Object, default: () => {}, required: true, }, }); const currentComponents = computed(() => { let _facotry;
// 这里可以用简单工厂模式的思想,写个方法 if (props.detail.productType === "promotion") { _facotry = new PromotionProductFactory(); } else { _facotry = new NormalProductFactory(); } const _computer = _facotry.createComputer(); return _computer.showComponents(); }); </script>
例2:日志系统中,创建不同类型的日志记录器。--- 代码差不多就不实现了。
抽象工厂模式
抽象工厂模式提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。它可以创建多个不同类型的产品族,符合开闭原则,但代码复杂度较高。
场景特点:适用于需要创建一组相关的对象,且这些对象之间存在一定的依赖关系或约束条件的场景。
具体应用:
例1:创建不同风格的 UI 组件。比如根据不同的主题创建一组相关的UI组件。
// ui-theme-factory.js
// 产品族 class Button { render() { throw new Error("该方法必须被重写!"); } } class LightButton extends Button { render() { console.log("LightButton"); } } class DarkButton extends Button { render() { console.log("DarkButton"); } }
// 产品族 class Input { render() { throw new Error("该方法必须被重写!"); } } class LightInput extends Input { render() { console.log("LightInput"); } } class DarkInput extends Input { render() { console.log("DarkInput"); } } class ThemeFactory { createButton() { throw new Error("该方法必须被重写!"); } createInput() { throw new Error("该方法必须被重写!"); } } class LightThemeFactory extends ThemeFactory { createButton() { return new LightButton(); } createInput() { return new LightInput(); } } class DarkThemeFactory extends ThemeFactory { createButton() { return new DarkButton(); } createInput() { return new DarkInput(); } } const lightFactory = new LightThemeFactory(); const lightButton = lightFactory.createButton(); const lightInput = lightFactory.createInput(); lightButton.render(); // LightButton lightInput.render(); // LightInput
例2:游戏中创建不同风格的游戏场景。比如沙漠场景、森林场景等。每个场景都有一组相关的游戏元素,如地形、怪物、道具等。
代码逻辑差不多,就不实现了。
四、如何选择
从上文中可以得出以下结论:
1、从适用场景考虑
简单工厂模式:产品种类较少,且不经常扩展
工厂方法模式:产品种类较多,需要频繁扩展
抽象工厂模式:需要同时创建多个关联产品
2、从产品类型和关联关系考虑
简单工厂模式:创建单一类型的对象,产品之间没有复杂的关联关系,只是根据不同的参数创建不同具体实现的对象。
工厂方法模式:侧重于创建单一类型的对象,方便扩展新的产品类型,产品之间相对独立,没有紧密的依赖关系。
抽象工厂模式:创建一组相关的对象,这些对象构成一个产品族,它们之间存在内在的关联和依赖关系。需要保证在同一产品族中的对象是相互兼容的。
3、从代码复杂度和维护成本考虑
简单工厂模式:代码结构简单,实现成本低,但当产品类型增多时,工厂类会变得臃肿,不符合开闭原则,维护成本会逐渐增加。
工厂方法模式:代码复杂度适中,通过将创建逻辑分散到具体的工厂子类中,提高了代码的可维护性和可扩展性,新增产品时对现有代码的影响较小。
抽象工厂模式:代码复杂度较高,需要定义多个抽象类和具体类,但它提供了更高层次的抽象和封装,能够很好地处理复杂的产品族创建问题,在复杂项目中可以有效降低整体的维护成本。