为了账号安全,请及时绑定邮箱和手机立即绑定

使用静态工厂方法来获取更新了一个字段的新实例是否正确?

使用静态工厂方法来获取更新了一个字段的新实例是否正确?

慕哥6287543 2022-01-12 14:31:20
我认为标题是自我描述的,但我会举一个例子来详细说明我的问题。我有一个DTO字段很少的CarDataTransferObj类(在我的例子中是一个类)。在另一个类(我们称之为类A)中,我需要多次创建该对象的新实例,但只更新一个字段(length我的示例中的字段)。GivenDTO在类中必须是不可变的A。由于类中有“许多”字段CarDataTransferObj,我考虑了以下方法(以避免在类中重复代码A):@Builderpublic class CarDataTransferObj {    private Integer id;    private String color;    private String manufacturer;    private String model;    private String uniqueIdNr;    private Integer nrOfDoors;    private EngineType engineType;    private Integer length;    private Integer safetyLevel;    public static CarDataTransferObj newInstanceWithUpdatedLength(final CarDataTransferObj car, final Integer newLength) {        return CarDataTransferObj.builder()                .id(car.getId())                .color(car.getColor())                .manufacturer(car.getManufacturer())                .model(car.getModel())                .uniqueIdNr(car.getUniqueIdNr())                .nrOfDoors(car.getNrOfDoors())                .engineType(car.getEngineType())                .length(newLength)                .safetyLevel(car.getSafetyLevel())                .build();    }}对我来说,这听起来像是对静态工厂方法的一点反模式使用。我不确定它是否可以接受,因此这个问题。以所呈现的方式使用静态工厂方法是一种反模式,应该避免吗?
查看完整描述

3 回答

?
犯罪嫌疑人X

TA贡献2080条经验 获得超4个赞

在我的搜索中,我没有遇到任何人将此1称为反模式。

但是,很明显,如果您尝试使用未专门实施以支持此操作模式的经典构建器来执行此操作....它将无法正常工作。例如,Wikipedia 文章中关于 Builder 设计模式的示例 CarBuilderImpl将状态放入急切创建的Car实例中。该build()方法只是返回该对象。如果您尝试以您建议的方式重用该构建器,您最终会修改一个Car已经构建的。

您还需要担心另一个问题。在我们修改 WikipediaCarBuilder示例以将实际车轮(而不是多个车轮)添加到Car正在建造的汽车中时,我们必须担心创建共享相同车轮的汽车。

您可以在构建器实现中解决这些问题,但尚不清楚收益是否超过成本。


如果你将这种想法转移到使用工厂方法来做这件事,你会得出一个稍微不同的结论。

  • 如果您将其作为“一次性”进行,那可能没问题。你有一个特定的需求,代码很笨拙......但问题也是如此。

  • 如果您需要对许多不同的参数或参数组合执行此操作,则无法扩展。

  • 如果创建的对象是可变的,那么这种方法在多线程环境中可能会出现问题,具体取决于您如何控制对用作模板的对象的访问。


1 - 对于某事物是否为反模式,没有明确的可衡量标准。这是一个见仁见智的问题。诚然,对于许多反模式来说,这种观点会有广泛的共识。


查看完整回答
反对 回复 2022-01-12
?
摇曳的蔷薇

TA贡献1793条经验 获得超6个赞

每次你想通过一个小的修改来制作一个新的副本时,通过一个构建器来构建一个全新的实例似乎有点低效。更重要的是,听起来您需要类不可变的地方与 A 类之类的地方隔离。为什么不尝试这样的事情:


public interface ICarDataTransferObject {

    public Integer GetId();

    public String GetColor();

    public String GetManufacturer();

    public String GetModel();

    public String GetUUID();

    public Integer GetDoorCount();

    public EngineType GetEngineType();

    public Integer GetLength();

    public Integer GetSafteyLevel();

}



public class CarDataTransferObject Implements ICarDataTransferObject {

    private Integer _id;

    private String _color;

    private String _manufacturer;

    private String _model;

    private String _uniqueIdNr;

    private Integer _nrOfDoors;

    private EngineType _engineType;

    private Integer _length;

    private Integer _safetyLevel;


    public Integer GetId() { return _id; }

    public void SetId(Integer id) { _id = id; }

    public String GetColor() { return _color; }

    public void SetColor(String color) { _color = color; }

    public String GetManufacturer() { return _manufacturer; }

    public void SetManufacturer(String manufacturer) { _manufacturer = manufacturer; }

    public String GetModel() { return _model; }

    public void SetModel(String model) { _model = model; }

    public String GetUUID() { return _uniqueIdNr; }

    public void SetUUID(String uuid) { _uniqueIdNr = uuid; }

    public Integer GetDoorCount() { return _nrOfDoors; }

    public void SetDoorCount(Integer count) { _nrOfDoors = count; }

    public EngineType GetEngineType() { return _engineType; }

    public void SetEngineType(EngineType et) { _engineType = et; }

    public Integer GetLength() { return _length; }

    public void SetLength(Integer length) { _length = length; }

    public Integer GetSafteyLevel() { return _safetyLevel; }

    public void SetSafteyLevel(Integer level) { _safteyLevel = level; }


    public CarDataTransferObject() {}

    public CarDataTransferObject(ICarDataTransferObject other) { ... }


    public ReadOnlyCarDataTransferObject AsReadOnly() { 

        return ReadOnlyCarDataTransferObject (this);

        }

    }

}


public class ReadOnlyCarDataTransferObject Implements ICarDataTransferObject  {

    private ICarDataTransferObject _dto = null;


    public Integer GetId() { return _dto.GetId(); }

    public String GetColor() { return _dto.GetColor(); }

    public String GetManufacturer() { return _dto.GetManufacturer(); }

    public String GetModel() { return _dto.GetModel(); }

    public String GetUUID() { return _dto.GetUUID(); }

    public Integer GetDoorCount() { return _dto.GetDoorCount(); }

    public EngineType GetEngineType() { return _dto.GetEngineType(); }

    public Integer GetLength() { return _dto.GetLength(); }

    public Integer GetSafteyLevel() { return _dto.GetSafteyLevel; }


    public ReadOnlyCarDataTransferObject (ICarDataTransferObject other) {

        _dto = other;

    }

}

现在,当您希望 A 类拥有任何人都无法修改的副本时,只需使用复制构造函数并仅公开该副本的只读版本。


public class A {

    ICarDataTransferObject _dto;

    ReadOnlyCarDataTransferObject _readOnlyDTO;


    public ICarDataTransferObject GetDTO() { return _readOnlyDTO; }


    public A(ICarDataTransferObject dto) {

        _dto = new CarDataTransferObject(dto);

        _readOnlyDTO = new ReadOnlyCarDataTransferObject(_dto);

    }

}

您通常在 .NET 应用程序中看到这种方法。


查看完整回答
反对 回复 2022-01-12
?
温温酱

TA贡献1752条经验 获得超4个赞

尽管您的静态方法是否是反模式尚有争议,但它肯定不会针对不同属性的组合进行扩展。尽管如此,即使它不是反模式,我认为有更好的方法来完成你所需要的。


有一个传统构建器模式的变体,它不是创建一个新的空构建器,而是接受一个已经构建的对象并创建一个已经初始化的构建器。以这种方式创建构建器后,您只需更改builder 中的length属性。最后,构建对象。用纯代码(没有龙目岛,对不起)它可能是这样的:


public class CarDataTransferObj {


    private Integer id;

    private String color;

    // other attributes omitted for brevity

    private Integer length;


    // Private constructor for builder

    private CarDataTransferObj(Builder builder) {

        this.id = builder.id;

        this.color = builder.color;

        this.length = builder.length;

    }


    // Traditional factory method to create and return builder

    public static Builder builder() {

        return new Builder();

    }


    // Factory method to create and return builder initialized from an instance

    public static Builder builder(CarDataTransferObj car) {

        Builder builder = builder();

        builder.id = car.id;

        builder.color = car.color;

        builder.length = car.length;

        return builder;

    }


    // getters


    public static class Builder {

        private Integer id;

        private String color;

        private Integer length;


        private Builder() { }


        public Builder withId(Integer id) { this.id = id; return this; }


        public Builder withColor(String color) { this.color = color; return this; }


        public Builder withLength(Integer length) { this.length = length; return this; }


        public CarDataTransferObj build() {

            return new CarDataTransferObj(this);

        }

    }

}

现在有了所有这些基础设施,您可以轻松地做您想做的事情:


CarDataTransferObj originalCar = ... // get the original car from somewhere


CarDataTransferObj newCar = CarDataTransferObj.builder(originalCar)

    .withLength(newLength)

    .build();

这种方法的优点是它可以很好地扩展(它可以用来改变参数的任何组合)。也许所有这些构建器的代码看起来都是样板,但我使用 IntelliJ 插件通过两次击键创建构建器(包括接受构建实例以创建初始化构建器的变体工厂方法)。


查看完整回答
反对 回复 2022-01-12
  • 3 回答
  • 0 关注
  • 189 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号