简体   繁体   English

将每个循环转换为流

[英]Convert for each loops to stream

I try to convert some for each loops to streams.我尝试将每个循环的一些转换为流。

I have following object relations:我有以下对象关系:

  • the main object is a collection of sensors主要对象是sensors的集合
  • each sensor has commands and attributes objects每个sensor都有commandsattributes对象
  • each command object has members objects每个command对象都有members对象
  • each attribute object has fields objects每个attribute对象都有fields对象

5 Loops 5 圈

List<SensorDTO> sensorDTOS = new ArrayList();

// Loop 1
for (Sensor sensor: sensors) {
    
    Set<CommandDTO> commandSet = new HashSet<>();
    // Loop 2
    for (Command c : sensor.getCommands()) {
        Set<MemberDTO> memberSet = new HashSet<>();
        // Loop 3
        for (Member m : c.getMembers()) {
            memberSet.add(new MemberDTO(m.getName()));
        }
        commandSet.add(new CommandDTO(c.getName(),memberSet));
    }
    
    Set<AttributeDTO> attributeSet = new HashSet<>();
    // Loop 4
    for (Attribute a : sensor.getAttributes()) {
        Set<FieldDTO> fieldSet = new HashSet<>();
        // Loop 5
        for (Field f : a.getFields()) {
            fieldSet.add(new FieldDTO(f.getName()));
        }
        attributeSet.add(new AttributeDTO( a.getName(), fieldSet));
    }

    SensorDTO sensorDTO = new SensorDTO(attributeSet, commandSet);
    sensorDTOS.add(sensorDTO);
}

Attempt using stream().forEach尝试使用stream().forEach

The only I have accomplished is to print the inner object.我唯一完成的是打印内部对象。

sensors.stream()
    .forEach(s-> { 
        s.getCommands().stream().forEach( c -> {
            c.getMembers().stream().forEach( m -> {
                 System.out.println(m);
            });
        });
    });

How to make a list of sensors to return as a list of sensorDTOS ?如何制作一个sensors列表以作为sensorDTOS列表返回?

List<SensorDTO> sensorDTOS = sensors.stream()
.map(sensor -> {
    Set<CommandDTO> commandSet = sensor.getCommands().stream()
        .map(command -> {
            Set<MemberDTO> memberSet = command.getMembers().stream()
                .map(member -> new MemberDTO(member.getName()))
                .collect(Collectors.toSet());
            return new CommandDTO(command.getName(), memberSet);
        })
        .collect(Collectors.toSet());
    Set<AttributeDTO> attributeSet = sensor.getAttributes().stream()
        .map(attribute -> {
            Set<FieldDTO> fieldSet = attribute.getFields().stream()
                .map(field -> new FieldDTO(field.getName()))
                .collect(Collectors.toSet());
            return new AttributeDTO(attribute.getName(), fieldSet);
        })
        .collect(Collectors.toSet());
    return new SensorDTO(attributeSet, commandSet);
})
.collect(Collectors.toList());

Use a mapper class使用映射器类

Then split 5 responsibilities into map-methods:然后将 5 个职责拆分为映射方法:

  1. map Sensor to SensorDTO (former Loop 1)Sensor映射到SensorDTO (前循环 1)
  2. map Sensor#commands to Set<CommandDTO> (former Loop 2)Sensor#commands映射到Set<CommandDTO> (前循环 2)
  3. map Command#members to Set<MemberDTO> (former Loop 3)Command#members映射到Set<MemberDTO> (前循环 3)
  4. map Sensor#attributes to Set<AttributeDTO> (former Loop 4)Sensor#attributes映射到Set<AttributeDTO> (前循环 4)
  5. map Attribute#fields to Set<FieldDTO> (former Loop 5)Attribute#fields映射到Set<FieldDTO> (前循环 5)
class Mapper {

    // former Loop 1
    static SensorDTO toDTO(Sensor sensor) {
        return new SensorDTO(
            Mapper.toDTO(sensor.getAttributes()),
            Mapper.toDTO(sensor.getCommands())
        );
    }

    // former Loop 2
    static Set<CommandDTO> toDTO(Collection<Command> commands) {
        return commands.stream()
            .map(Mapper::toCommandDTO)
            .collect(Collectors.toSet());
    }

    static CommandDTO toCommandDTO(Command c) {
        return new CommandDTO(c.getName(), Mapper.toDTO(c.getMembers()));
    }

    // former Loop 3        
    static Set<MemberDTO> toDTO(Collection<Member> members) {
        return members.stream()
            .map(m -> new MemberDTO(m.getName()))
            .collect(Collectors.toSet());
    }

    // former Loop 4
    static Set<AttributeDTO> toDTO(Collection<Attribute> attributes) {
        return attributes.stream()
            .map(Mapper::toAttributeDTO)
            .collect(Collectors.toSet());
    }

    static AttributeDTO toAttributeDTO(Attribute a) {
        return new AttributeDTO(a.getName(), Mapper.toDTO(a.getFields()));
    }

    // former Loop 5
    static Set<FieldDTO> toDTO(Collection<Field> fields) {
        return fields.stream()
            .map(f -> new FieldDTO(f.getName()))
            .collect(Collectors.toSet());
    }
    
}

Note: For illustrative brevity I omitted any access modifiers like public .注意:为了简洁起见,我省略了任何访问修饰符,如public

Then you can use the mapper simply like this:然后你可以像这样简单地使用映射器:

List<SensorDTO> dtos = sensors.stream()
    .map(Mapper::toDTO)
    .collect(Collectors.toList());

Why the mapper class?为什么是映射器类?

Instead of using one huge stream we broke it into small parts, each with its own responsibility to map.我们没有使用一个巨大的流,而是将其分成小部分,每个部分都有自己的映射责任。 Benefits are readability and maintainability.好处是可读性和可维护性。 Then it is also easier to test.那么测试起来也比较容易。

Usually you would use anobject-relational mapping (ORM) framework to convert from DTO to domain model and back (like MapStruct, Orika, ModelMapper, Dozer).通常,您会使用对象关系映射 (ORM)框架在 DTO 和域模型之间来回转换(如 MapStruct、Orika、ModelMapper、Dozer)。

This Mapper class with its decomposed mapping methods is a good preparation to ease migration to a real ORM framework in the future.这个Mapper类及其分解的映射方法为将来轻松迁移到真正的 ORM 框架做了很好的准备。

See also:也可以看看:

The very first important thing to point out the imperative code with lots of nested loops presented in the Question as well attempts to re-implement it with Streams that can be observed in some other Answers are glaring examples of the violation of the first GRASP principle the Information expert principle .第一件重要的事情是指出问题中出现的带有大量嵌套循环的命令式代码,以及尝试使用可以在其他一些答案中观察到的 Streams 重新实现它是违反第一个 GRASP 原则明显例子信息专家原则

Using the principle of information expert, a general approach to assigning responsibilities is to look at a given responsibility, determine the information needed to fulfill it, and then determine where that information is stored.使用信息专家原则,分配职责的一般方法是查看给定的职责,确定履行职责所需的信息,然后确定该信息的存储位置。

This will lead to placing the responsibility on the class with the most information required to fulfill it.这将导致将责任放在具有履行职责所需的最多信息的类上。

In other words, the class that hold the information should be responsible for processing it .换句话说,持有信息的类应该负责处理它

The advantage of this approach is better maintainability of code because data is being processed in one place (in the owning class) in isolation (ie without interfering with other code).这种方法的优点是代码的可维护性更好,因为数据在一个地方(在所属类中)被隔离地处理(即不干扰其他代码)。 Such logic is easy to extend, reuse and to test.这样的逻辑很容易扩展、重用和测试。

It worth to point out that a good alternative to would make use of the frameworks like MapStruct to manage the logic of transformation between domain classes and DTOs.值得指出的是,一个很好的替代方案是使用MapStruct等框架来管理域类和 DTO 之间的转换逻辑。

Here's how the method for transforming a List<Sensor> into a List<SensorDTO> might look like:以下是将List<Sensor>转换为List<SensorDTO>的方法可能如下所示:

public static List<SensorDTO> toSensorDTO(List<Sensor> sensors) {
    
    return sensors.stream().map(Sensor::toDTO).toList();
}

Each class does its potion of work, transforming the data it owns via method toDto() .每个类都完成自己的工作,通过方法toDto()转换它拥有的数据。

public static class Sensor {
    private Set<Command> commands;
    private Set<Attribute> attributes;

    public SensorDTO toDTO() {
    
        return Stream.of(this)
            .collect(Collectors.teeing(
                Collectors.flatMapping(s -> s.getCommands().stream()
                    .map(Command::toDTO), Collectors.toSet()),
                Collectors.flatMapping(s -> s.getAttributes().stream()
                    .map(Attribute::toDTO), Collectors.toSet()),
                SensorDTO::new
            ));
    }
}

public static class Command {
    private String name;
    private Set<Member> members;
    
    public CommandDTO toDTO() {
        Set<MemberDTO> memberDTOSet = members.stream()
            .map(Member::toDTO)
            .collect(Collectors.toSet());
        
        return new CommandDTO(name, memberDTOSet);
    }
}

public static class Member {
    private String name;

    public MemberDTO toDTO() {
        return new MemberDTO(name);
    }
}

public static class Attribute {
    private String name;
    private Set<Field> members;

    public AttributeDTO toDTO() {
        Set<FieldDTO> fieldDTOSet = members.stream()
            .map(Field::toDTO)
            .collect(Collectors.toSet());
    
        return new AttributeDTO(name, fieldDTOSet);
    }
}

public static class Field {
    private String name;
    
    public FieldDTO toDTO() {
        return new FieldDTO(name);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM