• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

使用Iterator模式将对象转成String

php 搞代码 4年前 (2022-01-04) 37次浏览 已收录 0个评论

操纵JSOM、XML、Java bean等对象时你可能最先想到访问者模式。但是使用访问者模式很难从调用代码控制回调。比如,不能有条件的从所有回调的子分支和叶子节点跳过某个分支。解决这个问题就可以使用Iterator模式遍历整个对象,生成易于开发者阅读和调试的字符串。该迭代器具备一定的通用性,我在使用XPath查找Java对象和在StackHunter中记录异常的工具中都用到了它。

API

本文主要介绍的两个类:StringGenerator和ObjectIterator。

字符串生成器

StringGenerator工具类将对象转化为字符串,使对象可读性更好。可以用它来实现类的toString方法或者把对象的字符串表达作为日志调试代码:

package com.stackhunter.util.tostring.example; import com.stackhunter.example.employee.Department;import com.stackhunter.example.employee.Employee;import com.stackhunter.example.employee.Manager;import com.stackhunter.example.people.Person; import com.stackhunter.util.tostring.StringGenerator; public class StringGeneratorExample {     public static void main(String[] args) {        Department department = new Department(5775, "Sales")        .setEmployees(                new Employee(111, "Bill", "Gates"),                 new Employee(222, "Howard", "Schultz"),                 new Manager(333, "Jeff", "Bezos", 75000));         System.out.println(StringGenerator.generate(department));        System.out.println(StringGenerator.generate(new int[] { 111, 222, 333 }));        System.out.println(StringGenerator.generate(true));    } }

StringGenerator.generate()将department,数组和boolean值进行格式化输出。

com.stackhunter.example.employee.Department@129719f4  deptId = 5775  employeeList = java.util.ArrayList@7037717a    employeeList[0] = com.stackhunter.example.employee.Employee@17a323c0      firstName = Bill      id = 111      lastName = Gates    employeeList[1] = com.stackhunter.example.employee.Employee@57801e5f      firstName = Howard      id = 222      lastName = Schultz    employeeList[2] = com.stackhunter.example.employee.Manager@1c4a1bda      budget = 75000.0      firstName = Jeff      id = 333      lastName = Bezos  name = Sales[I@39df3255  object[0] = 111  object[1] = 222  object[2] = 333true

对象迭代器

ObjectIterator使用迭代器模式遍历对象的属性,以键值对形式保存。对象中的Java bean、集合、数组及map都要进行迭代。ObjectIterator也会考虑到对象之间循环引用的处理。

package com.stackhunter.util.tostring.example; import com.stackhunter.example.employee.Department;import com.stackhunter.example.employee.Employee;import com.stackhunter.example.employee.Manager;import com.stackhunter.util.objectiterator.ObjectIterator; public class ObjectIteratorExample {     public static void main(String[] args) {        Department department = new Department(5775, "Sales")        .setEmployees(                new Employee(111, "Bill", "Gates"),                 new Employee(222, "Howard", "Schultz"),                 new Manager(333, "Jeff", "Bezos", 75000));         ObjectIterator iterator = new ObjectIterator("some department", department);                 while (iterator.next()) {            System.out.println(iterator.getName() + "=" + iterator.getValueAsString());        }    } }

通过遍历整个对象生成键值对的集合。使用getValueAsString()方法而不是toString()格式化输出。对于原始类型、包装类型、字符串、日期和枚举使用原始的toString()实现。对于其他类型输出类名和hash值。

ObjectIterator.getDepth()会增加缩进,输出更易读。调用next()之前使用nextParent()缩短当前分支跳跃到下一属性。

some department=com.stackhunter.example.employee.Department@780324ffdeptId=5775employeeList=java.util.ArrayList@6bd15108employeeList[0]=com.stackhunter.example.employee.Employee@22a79c31firstName=Bill...

Jav来源gaodaimacom搞#代%码网a对象迭代器的具体实现

实现iterator模式的第一步是创建通用的迭代器接口:IObjectIterator。无论遍历的对象是Java bean、数组还是map都可以使用该接口。

public interface IObjectIterator {    boolean next();    String getName();    Object getValue();}

使用该接口可以按照单一顺序依次获取当前属性的name和value。

实现了IObjectIterator的类用来处理某一种类型的对象。大多数类调用getName()返回名称前缀。ArrayIterator使用了元素的索引:return name + "[" + nextIndex + "]";。

属性迭代器

PropertyIterator可能是最重要的迭代类。它使用Java bean introspection读取对象属性,将它们转化为键值对序列。

public class PropertyIterator implements IObjectIterator {     private final Object object;    private final PropertyDescriptor[] properties;    private int nextIndex = -1;    private PropertyDescriptor currentProperty;     public PropertyIterator(Object object) {        this.object = object;        try {            BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());            properties = beanInfo.getPropertyDescriptors();        } catch (RuntimeException e) {            throw e;        } catch (Exception e) {            throw new RuntimeException(e.getMessage(), e);        }    }     @Override    public boolean next() {        if (nextIndex + 1 >= properties.length) {            return false;        }         nextIndex++;        currentProperty = properties[nextIndex];        if (currentProperty.getReadMethod() == null || "class".equals(currentProperty.getName())) {            return next();        }        return true;    }     @Override    public String getName() {        if (currentProperty == null) {            return null;        }        return currentProperty.getName();    }     @Override    public Object getValue() {        try {            if (currentProperty == null) {                return null;            }            return currentProperty.getReadMethod().invoke(object);        } catch (RuntimeException e) {            throw e;        } catch (Exception e) {            throw new RuntimeException(e.getMessage(), e);        }    } }

数组迭代器

ArrayIterator通过反射得到数组的长度,进而检索每个数据元素。ArrayIterator不关心从getValue()方法返回值的具体细节。它们一般情况下被传递给PropertyIterator。

public class ArrayIterator implements IObjectIterator {     private final String name;    private final Object array;    private final int length;    private int nextIndex = -1;    private Object currentElement;     public ArrayIterator(String name, Object array) {        this.name = name;        this.array = array;        this.length = Array.getLength(array);    }     @Override    public boolean next() {        if (nextIndex + 1 >= length) {            return false;        }         nextIndex++;        currentElement = Array.get(array, nextIndex);        return true;    }     @Override    public String getName() {        return name + "[" + nextIndex + "]";    }     @Override    public Object getValue() {        return currentElement;    } }

集合迭代器

CollectionIterator与ArrayIterator非常相似。使用java.lang.Iterable调用它的Iterable.iterator()方法初始化内部迭代器。

Map迭代器

MapIterator遍历java.util.Map的entry。它并不深入到每个entry的键值对,这个工作由MapEntryIterator类完成。

public class MapIterator implements IObjectIterator {     private final String name;    private Iterator<?> entryIterator;    private Map.Entry<?, ?> currentEntry;    private int nextIndex = -1;     public MapIterator(String name, Map<?, ?> map) {        this.name = name;        this.entryIterator = map.entrySet().iterator();    }     @Override    public boolean next() {        if (entryIterator.hasNext()) {            nextIndex++;            currentEntry = (Entry<?, ?>) entryIterator.next();            return true;        }        return false;    }     ... }

Map Entry迭代器

MapEntryIterator处理java.util.Map的单个entry。它只返回两个值:entry的键和值。与ArrayIterator及其他的类似,如果是复杂类型的话,它的结果可能最终传递给PropertyIterator,作为Java bean处理。

根迭代器

RootIterator返回单个元素——初始节点。可以把它想成XML文件的根节点。目的是发起整个遍历过程。

整合

ObjectIterator类作为门面角色(Facade),包装了所有的遍历逻辑。它根据最后一次getValue()的返回值类型决定哪个IObjectIterator的子类需要实例化。当子迭代器在内部创建时它在栈中保存当前迭代器的状态。它也暴露了getChild()和getDepth()方法为调用者展示当前进度。

private IObjectIterator iteratorFor(Object object) {    try {        if (object == null) {            return null;        }         if (object.getClass().isArray()) {            return new ArrayIterator(name, object);        }         if (object instanceof Iterable) {            return new CollectionIterator(name, (Iterable<?>) object);        }         if (object instanceof Map) {            return new MapIterator(name, (Map<?, ?>) object);        }         if (object instanceof Map.Entry) {            return new MapEntryIterator(name, (Map.Entry<?, ?>) object);        }         if (isSingleValued(object)) {            return null;        }         return new PropertyIterator(object);    } catch (RuntimeException e) {        throw e;    } catch (Exception e) {        throw new RuntimeException(e.getMessage(), e);    } }

字符串生成器的实现

已经看到如何遍历对象中的所有属性。最后的工作就是让输出更加美观,以及增加一些限制条件(比如不能创建一个GB级大小的字符串)。

public static String generate(Object object) {    String s = "";     ObjectIterator iterator = new ObjectIterator("object", object);     ...     while (iterator.next()) {        if (s.length() >= MAX_STRING_LENGTH) {            return s;        }         if (iterator.getChild() >= MAX_CHILDREN) {            iterator.nextParent();            continue;        }         String valueAsString = iterator.getValueAsString();         s += System.lineSeparator();        s += indent(iterator.getDepth()) + truncateString(iterator.getName());        if (valueAsString == null) {            s += " = null";        } else {            s += " = " + truncateString(valueAsString);        }    }     return s;}

代码第21行完成了格式化,增加了缩进和层次结构使显示更美观。

同时增加了一些限制条件:

第9行——限制字符串长度在16k内。

第13行——限制任何父节点下子节点数量小于64.

第21&25行——限制键和值长度在64个字符。

总结

本文介绍了如何使用迭代器模式遍历包含各种复杂属性的对象。关键是将每种类型的迭代委派给各自的类实现。可以在你自己的软件中使用这两个工具。


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:使用Iterator模式将对象转成String

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址