zoukankan      html  css  js  c++  java
  • [原]实例-简单设计&精简代码&复用代码

    引言

    本文以实际项目为例谈一谈我个人对于软件开发的理解,偏细节
     
    软件项目B
    基于.net平台,使用WPF框架,c#语言,MVVM模式开发的桌面软件
    该软件支持可视化的设计器功能,允许所见即所得的方式为页面添加文字、图像等元素。可对元素进行编译解析,生成对应的二进制数据下发至下位机,本文不对软件整体设计做介绍,仅列举部分设计及编码细节进行介绍
     

    独立的Model层数据类型

    Model层作为独立的数据访问层,数据类型定义保持独立,仅记录数据本身,ui无关
    结构如下图
    BGProject 项目类型,组合多个BGDiargam视图对象
    BGDiagram视图类型,组合多个BGElement元素对象,存在多个派生元素类型
     
    View层在使用数据时,可封装视图数据类型组合Model数据类型,以记录其他UI相关数据
     

    适度封装以简化代码

    编译过程需对文字、图片等做不同处理
    初期实现时仅实现了一个TextCompiler,后续陆续实现ImageCompiler等,遂提取抽象基类CompilerBase
    形成如下结构
     
    Compile方法由各个派生类自行实现编译逻辑
    上层编译逻辑的实现,简单使用多态,如下
        public bool Compile(BGProject project, out String errorMessage)
        {
            TextCompiler textCompiler = new TextCompiler(project);
            ImageCompiler imageCompiler = new ImageCompiler(project);
            XxxCompiler xxxCompiler = new XxxCompiler(project);
    
            foreach (CompilerBase compiler in
                new CompilerBase[] {textCompiler, imageCompiler, XxxCompiler})
            {
                compiler.Compile();
                if (!compiler.Validate(out errorMessage))
                {
                    return false;
                }
            }
            
            // ...
        }

    Don't Repeat Yourself 复用代码

    每一种数据的编译逻辑中,都需要遍历相应类型的元素,因此考虑将元素遍历逻辑独立出来
    为基类TravelCompilerBase添加如下方法
            protected static void TravelElements<T>(BGProject project, BGElementType elementType, Action<T> action)
                where T : BGElement
            {
                foreach (BGDiagram diagram in project.Diagrams)
                {
                    foreach (T element in
                        diagram.Elements.Where(e => e.ElementType == elementType))
                    {
                        action(element);
                    }
                }
            }
    处理逻辑通过action参数传递进来
     
    TextCompiler中编译文字元素时,调用上述方法,通过lambda表达式生成匿名方法完成处理逻辑
                TravelElements<BGTextElement>(Project, BGElementType.Text,
                                              element =>
                                                  {
                                                      //.... 针对目标元素做相应处理
                                                  });

    处理该特定问题,这里使用委托的方式,泛型化使其易于使用,你也可以使用TemplateMethod模式

     

    合宜地使用静态类型封装基本工具类型

    静态类型是一种良好组织独立工具method的方式
    许多不专业的程序员常将静态类型作为存储全局对象的容器,这其实是在破坏软件结构。应尽一切可能避免使用静态类型变量
     

    序列化工具类

    项目中涉及数据序列化到本地文件,直接使用如下工具类型

        public static class SerializeUtility
        {
            public static void BinarySave<T>(String filePath, T obj)
            {
                using (Stream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    IFormatter formatter = new BinaryFormatter();
                    formatter.Serialize(stream, obj);
                }
            }
    
            public static T BinaryLoad<T>(String filePath)
            {
                return BinaryLoad<T>(filePath, null);
            }
    
            public static T BinaryLoad<T>(String filePath, SerializationBinder serializationBinder)
            {
                if (!File.Exists(filePath))
                {
                    return default(T);
                }
    
                using (Stream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
                {
                    IFormatter formatter = new BinaryFormatter();
                    if (serializationBinder != null)
                    {
                        formatter.Binder = serializationBinder;
                    }
                    return (T)formatter.Deserialize(stream);
                }
            }
        }

    int与byte数组转换工具类

    涉及到传输数据至下位机,考虑数据存储格式,编写如下工具类,支持littleEnding与bigEnding

        // Created by Ant 2014-4-30
        public static class BytesConverterUtility
        {
            public static byte[] GetBytes(int value, int length, bool isLittleEndian = true)
            {
                if (value < 0)
                {
                    throw new ArgumentException("value不能为负数");
                }
    
                if (length > 4)
                {
                    throw new ArgumentException("length不能>4");
                }
    
                var rawBytes = BitConverter.GetBytes(value);
    
                if (rawBytes.Length < length)
                {
                    throw new ApplicationException(
                        String.Format("BitConverter.GetBytes返回的字符数{0}小于目标字符数{1}", rawBytes.Length, length));
                }
    
                var bytes = new byte[length];
    
                if (BitConverter.IsLittleEndian != isLittleEndian)
                {
                    Array.Reverse(rawBytes);
                    Array.Copy(rawBytes, rawBytes.Length - length,
                               bytes, 0, length);
                }
                else
                {
                    Array.Copy(rawBytes, bytes, length);
                }
    
                return bytes;
            }
    
            public static int ToInt(byte[] bytes, int offset, int length, bool isLittleEndian = true)
            {
                if (length == 1)
                {
                    return bytes[offset];
                }
    
                var tempBytes = new byte[length];
    
                Array.Copy(bytes, offset, tempBytes, 0, length);
    
                if (!isLittleEndian)
                {
                    Array.Reverse(tempBytes);
                }
    
                switch (length)
                {
                    case 2:
                        // 特殊处理,转换为无符号int类型,返回时自动转换为Int32
                        return BitConverter.ToUInt16(tempBytes, 0);
                    case 4:
                        // 注意,这里将数据转换为有符号int类型
                        return BitConverter.ToInt32(tempBytes, 0);
                    default:
                        throw new ArgumentException("length 长度非标准值");
                }
            }
        }

    工具类型的方便之处在于其独立性,几乎无外部依赖,不需要考虑对其进行初始化,拿来就可以直接使用

  • 相关阅读:
    辅助方法
    扩展方法学习发展之路
    对IOC模式与容器Autofac在MVC里面的一些用法
    Qlikview Session Recovery
    SNOY VAIO 索尼电脑亮度自动变暗
    EXCEL2010数据挖掘插件 下载地址
    Ubuntu20.04 源码安装完应用 如何将应用程序添加到启动器
    flask + flask_restplus+ blueprints 脚手架
    闭包
    操作方法
  • 原文地址:https://www.cnblogs.com/gods/p/4201530.html
Copyright © 2011-2022 走看看