zoukankan      html  css  js  c++  java
  • 使用函数接口和枚举实现配置式编程(Java与Scala实现)

    概述###

    做报表时,有时需要根据不同的业务生成不同的报表。这样,需要能够动态地配置列字段,并根据列字段来输出对应的报表。使用函数接口结合枚举可以比较优雅地实现配置式编程。

    问题描述如下:

    假设有对象 Student, Teacher ,它们均有属性 Id, Name, Able 。 要求:(1) 打印这些 Student, Teacher 的字段 (Id, Name) 的内容 ; (2) 打印这些 Student, Teacher 的字段 (Name, Able) 的内容。

    Java代码示例###

    直接上代码。应该能看懂。 需要 Java1.8 才能正常运行。

    接口定义####

    package zzz.study.function;
    
    /**
     * Created by shuqin on 17/3/30.
     */
    public interface Person {
    
        String getId();
        String getName();
    
        String able();
    }
    
    

    对象定义####

    类 Student :

    package zzz.study.function;
    
    /**
     * Created by shuqin on 17/3/30.
     */
    public class Student implements Person {
    
        private String studentId;
        private String name;
        private String able;
    
        public Student(String studentId, String name, String able) {
            this.studentId = studentId;
            this.name = name;
            this.able = able;
        }
    
        @Override
        public String getId() {
            return studentId;
        }
    
        @Override
        public String getName() {
            return name;
        }
    
        @Override
        public String able() {
            return able;
        }
    }
    

    类 Teacher :

    package zzz.study.function;
    
    /**
     * Created by shuqin on 17/3/30.
     */
    public class Teacher implements Person {
    
        private String teacherId;
        private String name;
        private String able;
    
        public Teacher(String teacherId, String name, String able) {
            this.teacherId = teacherId;
            this.name = name;
            this.able = able;
        }
    
        @Override
        public String getId() {
            return teacherId;
        }
    
        @Override
        public String getName() {
            return name;
        }
    
        @Override
        public String able() {
            return able;
        }
    }
    

    字段定义配置####

    字段定义配置是核心。 这里结合了枚举和函数式接口。这里之所以写成 FieldEnum(fieldName, fieldTitle, fieldValueGetMethod) 的定义方式,是为了便于管理。同样可以采用两个 Map 来实现:Map<fieldName, fieldTitle>, Map<fieldName, fieldValueGetMethod>,这样更适用于 Java1.6 , 不过要把两个 Map 拼起来才是完整的字段定义视图。 Person::getName 是方法引用,(Person p) -> p.getName() 的简写形式。

    package zzz.study.function;
    
    import java.util.function.Function;
    
    /**
     * Created by shuqin on 17/3/30.
     */
    public enum FieldConf {
    
        Id("Id", "编号", Person::getId),
        Name("Name", "姓名", Person::getName),
        Able("Able", "能力", Person::able);
    
        private String name;
        private String title;
        private Function<Person, String> method;
    
        FieldConf(String name, String title, Function<Person,String> method) {
            this.name = name;
            this.title = title;
            this.method = method;
        }
    
        public String getName() {
            return name;
        }
    
        public String getTitle() {
            return title;
        }
    
        public Function<Person, String> getMethod() {
            return method;
        }
    }
    
    

    字段定义伴生类####

    FieldConfAccompany 是 FieldConf 的伴生类, 从 Scala 的伴生对象借鉴而来,体现了 类变量、方法 与 实例变量、方法 分离的设计思想,使得两者各司其责, 都比较简洁。

    package zzz.study.function;
    
    import java.util.*;
    
    /**
     * Created by shuqin on 17/3/30.
     * FieldConf 的伴生对象, 从Scala借鉴而来
     */
    public class FieldConfAccompany {
    
        private static Map<String, FieldConf> fieldConfMap = new HashMap<String, FieldConf>();
        private static List<String> allFields = new ArrayList<>();
    
        static {
            for (FieldConf fc: FieldConf.values()) {
                fieldConfMap.put(fc.name(), fc);
                allFields.add(fc.getName());
            }
        }
    
        public static FieldConf getInstance(String name) {
            return fieldConfMap.get(name);
        }
    
        public static List<String> getAllFields() {
            return Collections.unmodifiableList(allFields);
        }
    }
    

    客户端使用####

    这里使用了 java8 Stream api 。 并没有什么特别的,只是针对列表的批量流式处理,具备延迟计算特性。

    package zzz.study.function;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    
    /**
     * Created by shuqin on 17/3/30.
     */
    public class Report {
    
        public static void main(String[] args) {
            report(Arrays.asList(new String[] {"Id", "Name"}), getPersons());
            report(Arrays.asList(new String[] {"Name", "Able"}), getPersons());
        }
    
        public static void report(List<String> fields, List<Person> persons) {
            String reportTitles = fields.stream().map(
                    field -> FieldConfAccompany.getInstance(field).getTitle()
            ).collect(Collectors.joining(","));
    
            List<String> rows = persons.stream().map(
                    p -> fields.stream().map(
                            field -> FieldConfAccompany.getInstance(field).getMethod().apply(p)
                    ).collect(Collectors.joining(","))
            ).collect(Collectors.toList());
    
            System.out.println(reportTitles);
            System.out.println(String.join("
    ",rows));
    
        }
    
        private static List<Person> getPersons() {
            Person s1 = new Student("s1", "liming", "Study");
            Person s2 = new Student("s2", "xueying", "Piano");
            Person t1 = new Teacher("t1", "Mr.Q", "Swim");
            Person t2 = new Teacher("t2", "Mrs.L", "Dance");
            return Arrays.asList(new Person[] {s1, s2, t1, t2});
        }
    
    }
    
    

    输出:

    编号,姓名
    s1,liming
    s2,xueying
    t1,Mr.Q
    t2,Mrs.L
    姓名,能力
    liming,Study
    xueying,Piano
    Mr.Q,Swim
    Mrs.L,Dance
    

    Scala代码示例###

    业务类####

    同样先定义业务类 Person, Student, Teacher 。 可以看到 Scala 的类定义比 Java 类定义的语法形式简洁不少,相当于语言层面实现了 lombok 的功能。

    package scalastudy.extend
    
    /**
      * Created by shuqin on 17/4/10.
      */
    trait Person {
      def getId: String
      def getName: String
      def getAble: String
    }
    
    class Student(studentId: String, name:String, able:String) extends Person {
      override def getId: String = studentId
      override def getName: String = name
      override def getAble: String = able
    }
    
    class Teacher(teacherId: String, name:String, able:String) extends Person {
      override def getId: String = teacherId
      override def getName: String = name
      override def getAble: String = able
    }
    

    字段配置####

    Scala 没有直接支持枚举类型,而是提供了 Enumeration 助手类。 可是这个类也不支持定义方法字段,因此,采用样例对象来模拟枚举功能。这里 apply 实现了静态构造器的功能,通过指定名称获取对应的样例对象。Scala 的 Case 功能非常强大,可以匹配常量、变量、容器结构及元素、类对象、正则表达式等各种对象,并赋值给相应的变量。

    package scalastudy.extend
    
    /**
      * Created by shuqin on 17/4/10.
      * Scala 实现枚举; 由于 Enumeration 不支持枚举含有方法字段,因此采用样例对象模拟实现。
      */
    sealed class FieldConf(name:String, title:String, able: (Person)=>String) {
      def getTitle = title
      def getAble = able
    }
    
    object FieldConf {
    
      def apply(name: String): FieldConf = {
        name match {
          case "Id" => Id
          case "Name" => Name
          case "Able" => Able
          case _ => Unknown
        }
      }
    }
    
    case object Id extends FieldConf("Id", "编号", p => p.getId)
    case object Name extends FieldConf("Name", "姓名", p => p.getName)
    case object Able extends FieldConf("Able", "能力", p => p.getAble)
    case object Unknown extends FieldConf("Unknown", "未知", p => "")
    
    

    报表输出####

    Scala 提供了相当多的助手方法,可以方便地实现常用功能,比如对列表拼接字符串。 在 Java 中就要难受地一次次编写无聊的 new StringBuilder , append, return sb.toString 这种套话, 而在 Scala 只要使用 mkString 即可,类似于 Python 的 join 方法。 Scala 的 lambda 表达式也很简洁,如果只有单变量的话,不必显式写出 p => doFor(p) 的形式, 而是直接可写成 doFor(_) 。

    package scalastudy.extend
    
    /**
      * Created by shuqin on 17/4/10.
      */
    object ExtendedReport extends App {
      launch()
    
      def launch(): Unit = {
        report(List("Id", "Name"), getPersons())
        report(List("Name", "Able"), getPersons())
      }
    
      def report(fields:List[String], persons:List[Person]):Unit = {
        val titles = fields.map(FieldConf(_).getTitle).mkString(",")
        println(titles)
        val rows = persons.map(
          p => fields.map(FieldConf(_).getAble.apply(p)).mkString(",")
        ).mkString("
    ")
        println(rows)
      }
    
      def getPersons():List[Person] = {
        List(new Student("s1", "liming", "Study"), new Student("s2", "xueying", "Piano"),
             new Teacher("t1", "Mr.Q", "Swim"), new Teacher("t2", "Mrs.L", "Dance"))
      }
    
    }
    
  • 相关阅读:
    [AS3]AMF3+JAVA的调用范例
    SmartFoxServer,多人flash游戏开发
    Flex2 发现之旅:在Flex中嵌入完整HTML页面
    让.Net 程序脱离.net framework框架运行
    ASP.NET 数据列表控件的分页总结
    中国移动飞信免费发短信API接口(第三方 Fetion API)[原创]
    Ubuntu下安装wine详细介绍
    SQL SERVER 2008 R2 序列号大全
    linux 下安装ATI 显卡驱动
    jdk 环境变量设置
  • 原文地址:https://www.cnblogs.com/lovesqcc/p/6649018.html
Copyright © 2011-2022 走看看