使用 Optional 类或 MayBe 类的抽象方法来解决反模式

由于冠状病毒的存在,可选的东西在空中,一切都变得可选,例如可选的公共聚会,可选的在家工作,可选的旅行等。

使用 Optional 类或 MayBe 类的抽象方法来解决反模式_第1张图片

我现在是时候谈论处理NULL引用的软件工程中真正的“ Optional ”了。

托尼·霍尔(Tony Hoare)坦言,他发明了空(Null)犯了数十亿美元的错误。 如果您还没有看过他的演讲,那么我建议您看一下Null-References-The-Billion-Dollar-Mistake 。

我将分享一些null的反模式 ,以及如何使用Optional或MayBe之类的抽象方法解决它。

在此示例中,我们将使用可以包含一些空值的简单值对象。

public class Person {
    final String firstName;
    final String lastName;
    
     final String email; // This can be null
    final String phone; //This can be null
}

该值对象的电子邮件和电话号码可以为空值。

方案:电子邮件和电话号码上的联系人

不使用可选
第一次尝试将基于检查null,如下所示

//Not using optional
        if (p.email != null) {
            System.out.println("Sending email to " + p.email);
        }

        if (p.phone != null) {
            System.out.println("Calling " + p.phone);
        }

这就是多年来所做的。 具有收集结果的另一种常见模式。

List p = searchPersonById("100");

        if (p.isEmpty()) {
            System.out.println("No result");
        } else {
            System.out.println("Person" + p.get(0));
        }

以错误的方式使用可选

Optional phone = contactNumber(p);
        Optional email = email(p);

        if (phone.isPresent()) {
            System.out.println("Calling Phone " + phone.get());
        }
        if (email.isPresent()) {
            System.out.println("Sending Email " + email.get());
        }

这样做好一点,但是通过在代码中添加if / else块,将Optional的所有好处都抛弃了。

永远快乐可选

//Always Happy
        Optional phone = contactNumber(p);
        Optional email = email(p);

        System.out.println("Calling Phone " + phone.get());
        System.out.println("Sending Email " + email.get());

很高兴感到高兴,但是当您尝试使用Optional时,您所做的假设很大,或者您不需要Optional。

嵌套属性可选
在这种情况下,我们将扩展Person对象并添加Home属性。 并非每个人都可以拥有房屋,因此最好不要使用该房屋。 让我们看看在这种情况下联系人场景如何工作

//Nested Property
        if (p.getHome() != null) {
            System.out.println("Sending Postal mail " + p.getHome().address);
        }


        if (p.getHome() != null && p.getHome().getInsurance() != null) {
            System.out.println("Sending Notification to insurance " + p.getHome().getInsurance().getAgency());
        }

在这里,代码将具有大量嵌套的空检查变得越来越糟。

基于优先级的默认
对于这种情况,我们首先尝试通过家庭住址与他人联系,如果该人不可用,则请通过办公地点与他人联系。

//Address has priority , first home and then Office

        if (p.home != null) {
            System.out.println("Contacted at home address " + p.home.address);
            return; // Magical return for early exit
        }

        if (p.office != null) {
            System.out.println("Contacted at office address " + p.office.address);
            return; // Magical return for early exit
        }

这种类型的场景需要使用提前控制流来尽早返回,并使代码难以理解和维护。

这些是一些常见模式,其中未使用可选选项或使用了错误的方式。

可选使用方式
让我们看看一些使用可选的好方法。

根据领域知识使属性可选
使属性成为可选属性非常容易。

public Optional getEmail() {
        return Optional.ofNullable(email);
    }

    public Optional getPhone() {
        return Optional.ofNullable(phone);
    }

是的,允许将其设为“可选”,没有人会为此而绞尽脑汁,并且可以毫无恐惧地随意这样做。 更改完成后,我们可以编写如下内容

//Use Optional
        p.getEmail().ifPresent(email -> System.out.println("Sending email to " + email));
        p.getPhone().ifPresent(phone -> System.out.println("Calling " + phone));

//Optional for Collection or Search type of request
 Optional

它看起来很整洁,第一步代码没有显式的if else在应用层。
使用一些Optional功能

//Use IfPresent & other cool things
        phone
                .filter(number -> hasOptIn(number))
                .ifPresent(number -> System.out.println("Calling Phone " + number));

        email
                .filter(m -> hasOptIn(m))
                .ifPresent(m -> System.out.println("Sending Email " + m));

可选就像流,我们得到所有功能映射,过滤器等支持。在上面的例子中,我们在联系之前正在检查OptIn。

永远快乐可选
“get”不检查将导致周日午夜运行时错误,因此建议使用ifPresent

//Don't do this
        System.out.println("Calling Phone " + phone.get());
        System.out.println("Sending Email " + email.get());

        //Use ifPresent to avoid runtime error
        phone.ifPresent(contact -> System.out.println("Sending email to " + contact));
        email.ifPresent(contact -> System.out.println("Calling " + contact));

嵌套可选

p.getHome().ifPresent(a -> System.out.println("Sending Postal mail " + a.address));

    p.getHome()
                .flatMap(Person.Home::getInsurance)
                .ifPresent(a -> System.out.println("Sending Notification to insurance " + a.agency));

Flatmap执行魔术,并处理home的空检查和转换保险对象。
基于优先级的默认

//Address has priority , first home and then Office

Optional address = Stream
                .of(person.getHome().map(Home::getAddress), person.getOffice().map(Office::getAddress))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .findFirst();

        address
                .ifPresent(add -> System.out.println("Contacting at address " + add));

这个例子是取家庭和办公室地址,并选择第一个有发送通知价值的地址。这种特殊的模式避免了大量的嵌套循环。

其他分支

可选的有什么不好呢

内存间接寻址

没有序列化

翻译自: https://www.javacodegeeks.com/2020/03/hands-on-optional-value.html

你可能感兴趣的:(java,javascript,设计模式,编程语言,vue)