从两个需求出发练就高质量代码--策略模式和命令模式

275 阅读3分钟

需求到模块

  • 需求
  1. 可输入数值的左右移动div,可以撤销和前进;
  2. 做一个画廊效果,图片数量由后端数据决定,排列顺序不一定,每个图片有这个图片的下标;
  • 模块
  1. 负责移动div的模块;
  2. 输入指令模块;
  3. 状态管理模块;
  • 模块设计

数据驱动思想

  • 从views驱动到data驱动
    • 视图驱动

+ 数据驱动

命令模式

  1. 定义:把具体的指令与实现隔离,对调用与执行解耦;
  2. 做法:将方法、数据封装到单一的对象中,对调用方与执行方进行解耦,达到职责分离的目的;
  3. 解决问题:降低调用部分与执行分布的解耦,让使用的时候只需要关心指令,而不需要关心具体的api;

如何更好的做一个功能

需求一实现

<template>
    <div class="moduleThink">
        <div>
            <button id="left" @click="toLeft">左移动</button>
            <input type="text" name="" id="num"/>
            <button id="right" @click="toRight">右移动</button>
        </div>
        <div class="wrapper">
            <div id="content">这是个移动的div</div>
        </div>
        <div>
            <button id="back" @click="goBack">后退</button>
            <button id="font" @click="goFont">前进</button>
        </div>
    </div>
</template>
<script>
    class DataManager {
        constructor() {
            this.stateArr = [{
                property: 'left',
                num: 0
            }];
            this.stateIndex = 0;
        }
        pushState(obj) {
            this.stateArr.push(obj);
            this.stateIndex = this.stateArr.length - 1;
        }
        getBack() {
            if (this.stateIndex > 0) {
                this.stateIndex--;
            }
        }
        getFont() {
            if (this.stateIndex < this.stateArr.length - 1) {
                this.stateIndex++;
            }
        }
        getData() {
            return this.stateArr[this.stateIndex];
        }
    }

    export default {
        name: 'ModuleThink',
        data() {
            return {

                dataManager: new DataManager()
            }
        },
        methods: {
            changeDiv: function() {
                var _data = this.dataManager.getData();
                var element = document.getElementById('content');
                element.style[_data.property] = _data.num;
            },
            toLeft: function() {
                var value = document.getElementById('num').value || 0;
                var left = document.getElementById('content').style.left || 0;

                this.handleDiv.excute({
                    property: 'left',
                    num: (parseInt(left) - parseInt(value)) + 'px'
                });
            },
            toRight: function() {
                var value = document.getElementById('num').value || 0;
                var left = document.getElementById('content').style.left || 0;
                this.handleDiv.excute({
                    property: 'left',
                    num: parseInt(value) + parseInt(left) + 'px'
                });
            },
            goBack: function() {
                this.handleDiv.excute('back');
            },
            goFont: function() {
                this.handleDiv.excute('font');
            }
        },
        mounted() {
            var vm = this;
            vm.handleDiv = (function() {
                return {
                    //命令模式
                    excute: function(commander) {
                        var commanderArr = ['back', 'font', 'left', 'right'];
                        if (typeof commander === 'object') {
                            vm.dataManager.pushState(commander);
                            vm.changeDiv();

                        } else {
                            var has = false;
                            if (commanderArr.indexOf(commander) != -1) {
                                has = true;
                                //策略模式
                                var state = {
                                    font: function() {
                                        vm.dataManager.getFont();
                                        vm.changeDiv();
                                    },
                                    back: function() {
                                        vm.dataManager.getBack();
                                        vm.changeDiv();
                                    }
                                };
                                state[commander]();
                            } else {
                                throw new Error('命令不合法');
                            }
                        }
                    }
                }
            })();
        }
    }
</script>
<style scoped>
    .wrapper {
        position: relative;
    }
    
    #content {
        position: absolute;
    }
</style>

效果图:

需求二实现

<template>
    <div id="imgContent">

    </div>
</template>
<script>
    /**
     * 命令解析模块---生成dom节点--具体的渲染.
     * 
     *
     * */
    class Picture {
        constructor(options) {
            this.html = '';
            this.options = options;
            this.render();
        }
        initData() {
            //各大框架里面都有使用的东西--与默认属性合并
            //1.有些属性不需要用户提供
            //2.防止用户漏传入
            var final = {};
            var defaultOptions = {
                data: [],
                id: document,
                sort: 'normal',
                size: [100, 100]
            };
            //适配器模式
            for (let item in defaultOptions) {
                if (this.options[item]) {
                    final[item] = this.options[item];
                    if (item === 'id') {
                        final[item] = document.getElementById(this.options[item]);
                    }
                } else {
                    final[item] = defaultOptions[item];
                }
            }
            return final;
        }
        initDOM(finalOptions) {
            //需要样式
            var styleArr = [{
                //包裹图片div样式
                float: 'left',
                width: finalOptions.size[0] + 'px',
                height: finalOptions.size[1] + 'px',
                position: 'relative'

            }, {
                //下标顺序的样式
                position: 'absolute',
                bottom: '0px',
                right: '0px',
                background: 'black',
                color: 'white',
                padding: '10px',
                width: '10px',
                height: '10px'

            }, {
                //图片样式
                width: '100%',
                height: '100%'
            }];
            var wraper = document.createElement('div');
            //排序图片
            //用策略模式
            var commanderHandle = {
                normal: function(arr) {
                    return arr;
                },
                reverse: function(arr) {
                    return arr.reverse();
                }
            }
            var data = commanderHandle[finalOptions.sort](finalOptions.data);
            data.forEach((url, index) => {
                var div = document.createElement('div');
                var img = document.createElement('img');
                var span = document.createElement('span');
                var styleObj = null;
                var handleDom = null;
                styleArr.forEach((style, index) => {
                    //享元模式
                    switch (index) {
                        case 0:
                            handleDom = div;
                            break;
                        case 1:
                            handleDom = span;
                            break;
                        case 2:
                            handleDom = img;
                            break;
                    }
                    for (var styleName in style) {
                        handleDom.style[styleName] = style[styleName];
                    }
                });
                img.setAttribute('src', url);
                div.appendChild(img);
                span.innerHTML = index + 1;
                div.appendChild(span);
                wraper.appendChild(div);
            });
            this.html = wraper;
        }
        renderDOM(element) {
            element.appendChild(this.html);
        }
        render() {
            var order = this.initData();
            this.initDOM(order);
            this.renderDOM(order.id);
        }
    }
    export default {
        name: 'Gallery',
        mounted() {
            var options = {
                id: 'imgContent',
                data: ['./static/img/1.jpg', './static/img/2.jpg', './static/img/3.jpg', './static/img/4.jpg', './static/img/5.jpg', './static/img/6.jpg']
            }
            var pic = new Picture(options);
        }
    }
</script>