透过源码学习设计模式6—策略模式与Comparator

简介:

定义算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户端。它首先定义不同的算法策略,然后客户端把算法策略作为它的一个参数。使用这种模式的一个不错的例子是Collection.sort()方法了,它使用Comparator对象作为参数。根据Comparator接口不同实现,对象会被不同的方法排序。

角色:

环境(Context):用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略,算法的直接访问,封装可能存在的变化;

抽象策略角色(Strategy):定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现

具体策略角色(ConcreteStrategy):实现了抽象策略定义的接口,提供具体的算法实现。

原则:

1、找出应用中可能需要变化之处,把他们独立出来。

2、针对接口编程,而不是针对实现

3、多用组合,少用继承

一般使用场景:

当系统能在几种算法中快速地切换,或系统中有一些类,它们仅行为不同时,或系统中存在多重条件选择语句时,可以考虑采用策略模式。

优缺点:

优点:

1、算法可以自由切换

2、避免使用多重条件判断

3、扩展性良好

缺点:

策略类数量增多,所有的策略类都需要对外暴露(不过可以通过其他如工厂方法模式修复这个缺陷)

JDK示例:

java.util.Collections方法:

sort(List<T> list, Comparator<? super T> c) 

参数:

list - 要排序的列表。

c - 确定列表顺序的比较器。null 值指示应该使用元素的 自然顺序。

根据指定Comparator产生的顺序对指定列表进行排序。此列表内的所有元素都必须能够使用指定Comparator相互比较。sort()方法是常用的策略设计模式示例之一,它以Comparator作为参数,我们可以为Comparator接口提供不同的实现。现在,Collections.sort()方法将根据传递给它的比较器实现对象排序。比如根据体重或大小来对Employee进行排序,您可以简单地创建一个Comparator来实现。

我们先来看下可与之对比的Comparable接口, 它为类的对象定义了默认顺序。这种默认顺序也称为对象的自然顺序。

class Employee implements Comparable<Employee> { 
    private int id; 
    private String name; 
    private double salary; 
    private LocalDate joiningDate; 
    public Employee(int id, String name, double salary, LocalDate joiningDate) { 
        this.id = id; 
        this.name = name; 
        this.salary = salary; 
        this.joiningDate = joiningDate; 
    } 
    public int getId() { 
        return id; 
    } 
    public void setId(int id) { 
        this.id = id; 
    } 
    public String getName() { 
        return name; 
    } 
    public void setName(String name) { 
        this.name = name; 
    } 
    public double getSalary() { 
        return salary; 
    } 
    public void setSalary(double salary) { 
        this.salary = salary; 
    } 
    public LocalDate getJoiningDate() { 
        return joiningDate; 
    } 
    public void setJoiningDate(LocalDate joiningDate) { 
        this.joiningDate = joiningDate; 
    } 
    // 根据ID进行比较
    /**
     \* @param   anotherEmployee - The Employee to be compared.
     \* @return  A negative integer, zero, or a positive integer as this employee
     \*          is less than, equal to, or greater than the supplied employee object.
    */ 
    @Override 
    public int compareTo(Employee anotherEmployee) { 
        return this.getId() - anotherEmployee.getId(); 
    } 
    // Two employees are equal if their IDs are equal
    @Override 
    public boolean equals(Object o) { 
        if (this == o) return true; 
        if (o == null || getClass() != o.getClass()) return false; 
        Employee employee = (Employee) o; 
        return id == employee.id; 
    } 
    @Override 
    public int hashCode() { 
        return Objects.hash(id); 
    } 
    @Override 
    public String toString() { 
        return "Employee{" + 
                "id=" + id + 
                ", name='" + name + '\'' + 
                ", salary=" + salary + 
                ", joiningDate=" + joiningDate + 
                '}'; 
    } 
} 

但是,如果您因为某个需求更改默认顺序呢?例如,如果您想根据前面示例中的Employee对象的名称而不是id对其进行排序,该怎么办?

你不能更改compareTo()函数的实现,因为不单单是你那块的特定需求, 它会影响所有地方的顺序。

此外,如果您正在处理预定义的Java类或在第三方库中定义的类,则无法更改默认顺序。例如,字符串对象的默认顺序是按字母顺序排列。但是如果你想根据它们的长度来排序呢?

对于这种情况,Java提供的正是Comparator接口。您可以定义一个Comparator并将其传递给像Collections.sort() 或 Arrays.sort()的排序函数。根据Comparator定义的顺序对对象排序。

public interface Comparator<T> { 
    int compare(T o1, T o2); 
} 
public class ComparatorExample { 
    public static void main(String[] args) { 
        List<Employee> employees = new ArrayList<>(); 
        employees.add(new Employee(1010, "Rajeev", 100000.00, LocalDate.of(2010, 7, 10))); 
        employees.add(new Employee(1004, "Chris", 95000.50, LocalDate.of(2017, 3, 19))); 
        employees.add(new Employee(1015, "David", 134000.00, LocalDate.of(2017, 9, 28))); 
        employees.add(new Employee(1009, "Steve", 100000.00, LocalDate.of(2016, 5, 18))); 
        System.out.println("Employees : " + employees); 
        // 根据名称排序
        Comparator<Employee> employeeNameComparator = new Comparator<Employee>() { 
            @Override 
            public int compare(Employee e1, Employee e2) { 
                return e1.getName().compareTo(e2.getName()); 
            } 
        }; 
        /*
        上面的Comparator可以用如下的lambda表达式写 =>
        employeeNameComparator = (e1, e2) -> e1.getName().compareTo(e2.getName());
        Java 8 Comparator默认方法甚至可以写的更简单
        employeeNameComparator = [Comparator.comparing(Employee::getName](http://Comparator.comparing(Employee::getName))
        */ 
        Collections.sort(employees, employeeNameComparator); 
        System.out.println("\nEmployees (Sorted by Name) : " + employees); 
        // 根据收入排序
        Comparator<Employee> employeeSalaryComparator = new Comparator<Employee>() { 
            @Override 
            public int compare(Employee e1, Employee e2) { 
                if(e1.getSalary() < e2.getSalary()) { 
                    return -1; 
                } else if (e1.getSalary() > e2.getSalary()) { 
                    return 1; 
                } else { 
                    return 0; 
                } 
            } 
        }; 
        Collections.sort(employees, employeeSalaryComparator); 
        System.out.println("\nEmployees (Sorted by Salary) : " + employees); 
        //根据入职时间排序
        Comparator<Employee> employeeJoiningDateComparator = new Comparator<Employee>() { 
            @Override 
            public int compare(Employee e1, Employee e2) { 
                return e1.getJoiningDate().compareTo(e2.getJoiningDate()); 
            } 
        }; 
        Collections.sort(employees, employeeJoiningDateComparator); 
        System.out.println("\nEmployees (Sorted by JoiningDate) : " + employees); 
    } 
} 

与状态模式区别

阅读 透过源码学习设计模式5—状态模式和Spring状态机 文末内容

java达人

ID:drjava

(长按或扫码识别)


感谢你的阅读,下面是一个抽奖链接按钮,9月22日晚上19点开奖,共10个红包,写文不易,感谢大家支持。

感谢大家一直以来的阅读、在看和转发,点我参与抽奖!点我参与抽奖

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值