现在是11月12号 我是5月份中旬步入这家公司的,差不多有半年之久了,中间花几个月看了设计模式,所以在实际业务中套用了一些设计模式来写代码,在此记录一下,所以写的不好 切勿喷/(ㄒoㄒ)/~~
1.什么是抽象工厂模式?
概念:提供一个接口以创建一系列相关或 互相依赖的对象,而无需指定他们的类。
换言之,使用者仅与抽象类定义的接口交互,而不适用特定的具体类接口
瞧瞧 瞧瞧这句话 有一说一 我在刚开始学的时候真的读不懂这句话。我来以菜鸡的角度理解一下这句话。
"仅调取 一个方法 他将返回 一系列(或更多) 的方法来产生对象"
重点在 "一系列" 可能还是有些不明不白 别着急 下面用具体业务实例 来引用抽象工厂来解决方法
抽象工厂优缺点
优点:可以创建多个产品进行多次组合,下面的实例会讲到,如果创建2个echarts 和 2个控制器那么他们组合的方方法有 2的立方,那么如果创建更多的产品,那么他们的组合那就太多了
缺点:一旦创建过的产品,他们之间进行了组合为了工厂,会使其耦合度变高,不能随随便便的修改其中任何的一个产品
前提:本人公司是在做toB项目,面向做大屏的业务及其的多,也是经验积累发现他们的共同之处,用该模式来重构我的 echarts 图表的业务
业务需求
用一个图表并且需要一个 条件或多个条件来与图表(echarts)的数据做关联 类似的原型图大概这样 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
如果说按平常的方式是如何调取echarts 图表 放在页面 ,并且还需要一个search或者select来控制echarts呢
xml
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<select name="" id="controller">
<option value="">数据1</option>
<option value="">数据2</option>
<option value="">数据3</option>
</select>
<div id="main" style="width: 300px;height:200px;"></div>
<script type="text/javascript">
document.querySelector('#controller').addEventListener('click', (event) => {
(省略代码)。。。。
test('#main', data)
})
// 基于准备好的dom,初始化echarts实例
function test(dom, data) {
var myChart = echarts.init(document.querySelector(dom));
// 指定图表的配置项和数据
var option = data;
myChart.setOption(option);
}
</script>
</body>
以上代码甚至还是只是创建一个echarts图表部分,
那么如果一个页面创建6个 12个呢,甚至更多呢 一旦突然一天后端告知,可能调取数据接口,加个字段,少个字段,好了,这一下全都得改了,这不得疯掉了
那为何使用抽象工厂模式呢
我们可以把 创建代码中的 controller 控制器部分(例如select,search)部分,抽象抽离出来
不需要每次都写代码,调用一个方法动态生成出来,并且与echarts 所关联起来,说到动态生成了,那当然我们的echarts 部分也需要如此
当然图表不只有echarts 还有其他大厂图表库,BizCharts,g2 等等 我们把select 和search 抽离出来 他们统称为什么?可以说是"控制组件" 把echarts g2 等等图表抽离出来统称为"图表组件"
创建 工厂类 和 产品类
javascript
// 抽象工厂 用来表明 该类 只是为了继承创建一个拥有 图表 和 控制器的 组件,它不能被用于生成具体实例
class createComponet {
creatcharts() {
throw new Error('抽象方法,用于实现创建 图表')
}
createCondition() {
throw new Error('抽象方法,用于实现创建 控制器')
}
}
// 抽象产品 该来 只是为了继承创建图表
class chart{
createChart(){
throw new Error('抽象方法 用于创建不用类型的图表')
}
}
// 抽象产品 该来 只是为了继承创建图表
class condition{
createCondition(){
throw new Error('抽象方法 用于创建不用类型的控制器')
}
}
创建 具体产品和 具体工厂 为什么 先创建产品,再创建工厂呢,那肯定 产品的组合 即 就是 工厂嘛
scala
// 创建 echarts 图表产品
class Echarts extends chart{
createChart(dom){
var myChart = echarts.init(document.querySelector(dom));
// 指定图表的配置项和数据
var option = {
tooltip: {},
legend: {
data:['销量']
},
xAxis: {
data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
};
myChart.setOption(option);
}
}
// 创建 G2 图表产品
class G21 extends chart{
createChart(dom){
const data = [
{ year: '1991', value: 15468 },
{ year: '1992', value: 16100 },
{ year: '1993', value: 15900 },
{ year: '1994', value: 17409 },
{ year: '1995', value: 17000 },
{ year: '1996', value: 31056 },
{ year: '1997', value: 31982 },
{ year: '1998', value: 32040 },
{ year: '1999', value: 33233 },
];
const chart = new G2.Chart({
container: `${dom}`,
autoFit: true,
});
chart.data(data);
chart.scale({
value: {
min: 10000,
nice: true,
},
year: {
range: [0, 1],
},
});
chart.tooltip({
showCrosshairs: true,
shared: true,
});
chart.axis('value', {
label: {
formatter: (val) => {
return (+val / 10000).toFixed(1) + 'k';
},
},
});
chart.area().position('year*value');
chart.line().position('year*value');
chart.render();
}
}
// 创建 搜索 产品
class Search extends condition{
createCondition(dom){
let condition = `<input type="text"><button>搜索</button>`
document.querySelector(dom).innerHTML = condition
}
}
// 创建 下拉框 产品
class select extends condition{
createCondition(dom){
let condition = `<select>
<option>数据1</option>
<option>数据2</option>
<option>数据3</option>
</select>`
document.querySelector(dom).innerHTML = condition
}
}
组合产品 产生 工厂
scala
// 创建一个由echarts 和search的 工厂
class createEcharts extends createComponet{
echarts(dom){
return new Echarts(dom)
}
search(dom){
return new Search(dom)
}
}
// 组合一个由g2 和select的 工厂
class createG2 extends createComponet {
G2(dom){
return new G21(dom)
}
select(dom){
return new select(dom)
}
}
那么 如何使用呢?
xml
<body>
<script src="https://gw.alipayobjects.com/os/lib/antv/g2/4.2.7/dist/g2.min.js"></script>
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.data-set-0.11.1/dist/data-set.js"></script>
<script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>
<script src="./test.js"></script>
<div class="box">
<div id="condition1">条件</div>
<div id="chart1" class="chart"></div>
</div>
<div class="box">
<div id="condition2">条件</div>
<div id="chart2" class="chart"></div>
</div>
<style>
.box {
width: 300px;
height: 300px;
border: 1px solid black;
}
.chart {
width: 100%;
height: 260px;
margin-top: 10px;
}
</style>
<script>
function setEcharts() {
let ctEcharts = new createEcharts()
let ECharts = ctEcharts.echarts()
ECharts.createChart('#chart1')
let search = ctEcharts.search()
search.createCondition('#condition1')
}
function setG2(){
let ctG2= new createG2()
let select = ctG2.select()
select.createCondition('#condition2')
let G2 = ctG2.G2()
G2.createChart('chart2')
}
setEcharts()
setG2()
</script>
</body>
当然 实际开发中的代码不止这些
其中的echarts 和 g2 中的option配置部分也是需要抽离出来,来保证单一原则
这么做也是为了 在每次触发search 或者 select 改变时 创建option 来导入 图表当中,来达到控制 图表 以上代码中 创建的 echarts 控制器为search
如果一天需求变了,需要的是一个下拉框select呢
这时候抽象工厂的好处就来了
csharp
// 重新组合 产品 创建具体工厂
class createEchartsAndSelect extends createComponet {
echarts(dom){
return new Echarts(dom)
}
select(dom){
return new select(dom)
}
}
function setEchartsAndSelect() {
let ctEcharts = new createEcharts()
let ECharts = ctEcharts.echarts()
ECharts.createChart('#chart1')
let select = ctEcharts.select()
select.createCondition('#condition1')
}
反之g2 图表也可以与其他控制器组合 或许说有一天select和search 都不满足业务中的需求了,又来一个 日期选择器的 控制器
scala
// 创建 搜索 产品
class date extends condition{
createCondition(dom){
具体业务代码.....
}
}
然后又可以重新组合多个工厂
这也就是抽象创建工厂的强大的地方
总结
其实在实际开发中业务逻辑也是特别复杂的,设计模式只是把一种复杂转换成了另一种复杂形式用于管理,在实际开发中我也会使用其他的设计模式,如果有时间我仍会总结出来,发帖记录一下
ps:但最近一直在学习计算机网络知识,看b站看书,发博客的日子也不定什么时候会发,群友也总说我怎么一直怎么卷
我的想法就是既然大佬对这种东西手拿把捏,我想我这种工作不久的菜鸡也得摸着大佬走过的路慢慢走过来,其实最近在工作中倒是有了新的开源想法,不知道在后面的日子会不会去实现,就这吧