使用多态替代If else或switch的更多尝试

在读到《代码整洁之道》里面说“使用多态代替if else和switch”的时候,刚好想到我们项目中有大量使用switch的情况,导致项目代码显得杂乱不堪,整个类超过1500行,刚接触这些代码的时候看得我想吐,试着能不能对代码进行一下重构,其中也碰到了一些问题,在此记录下来。


1.多条件的一般处理方式(使用switch)

现在考虑有这样一种简单场景,需要我们根据性别和年纪来找到合适的称呼。

<span style="font-size:18px;">/**
 * 性别
 */
public enum Sex {
	FEMALE,
	MALE
}</span>

<span style="font-size:18px;">/**
 * 年龄分类
 */
public enum Age {
	YOUNG,
	ADULT,
	OLD
}</span>
<span style="font-size:18px;">public class Person {
	private Sex sex;
	private Age age;

	//setter,getter,构造函数
}</span>
那给定一个Person,找到合适的称呼,可以写出如下代码:

<span style="font-size:18px;">public class MakeDecision {
	public static void main(String[] args) {
		Person person = new Person(Sex.FEMALE, Age.YOUNG);

		Age age = person.getAge();
		Sex sex = person.getSex();
		switch (sex) {
		case FEMALE:
			switch (age) {
			case YOUNG:
				System.out.println("小女孩");
				break;
			case ADULT:
				System.out.println("中年妇女");
				break;
			case OLD:
				System.out.println("老太婆");
				break;
			default:
				break;
			}
			break;

		case MALE:
			switch (age) {
			case YOUNG:
				System.out.println("小男孩");
				break;
			case ADULT:
				System.out.println("怪蜀黍");
				break;
			case OLD:
				System.out.println("老头儿");
				break;
			default:
				break;
			}
		}

	}

}</span>

这样符合我们的一般思维,下面用多态代替。


2.多态的形式

<pre name="code" class="java"><span style="font-size:18px;">/**
 * 基类
 */
public class Person {
	private Sex sex;
	private Age age;

	public void print() {

	}

	// setter,getter
}</span>

<span style="font-size:18px;">public class LittleGirl extends Person {

	@Override
	public void print() {
		System.out.println("小女孩");
	}
}</span>
<span style="font-size:18px;">public class Madam extends Person {

	@Override
	public void print() {
		System.out.println("中年妇女");
	}
}</span>

.......

那么问题来了,这样对于给定的Person,虽然使用了多态,是不是还是无法避免进行条件判断?你可能写出的代码如下:

<span style="font-size:18px;">public class MakeDecision {
	public static void main(String[] args) {
		Person person = new Person();
		person.setAge(Age.YOUNG);
		person.setSex(Sex.FEMALE);

		// 重构版本
		Person person1 = matchPerson(person);
		person1.print();
	}


	private static Person matchPerson(Person person) {


		Age age = person.getAge();
		Sex sex = person.getSex();
		switch (sex) {
		case FEMALE:
			switch (age) {
			case YOUNG:
				return new LittleGirl();
			case ADULT:
				return new Madam();
			case OLD:
				return new OldLady();
			}
		case MALE:
			//其他男人的称呼
		}
		return new LittleGirl();
	}
}</span>

对于这种组合条件的判断,使用多态还是无法避免条件判断,唯一的好处是,如果对于条件组合很多的情况,可以将各自的处理逻辑隔离开来,我们这里举例处理逻辑就只有一句System,out.println,但是真正情况可能包含更多判断和操作,只有做之后,就可以将这些判断和操作,分发到各自的子类当中去(此例中的LittleGirl、Madam等的print函数中)。


3.更多的尝试

为了解决上面的疑问,利用map和表驱动法来解决,不知道有没有更好的方案。

<span style="font-size:18px;">public class MakeDecision {
	private static final String[] table = { "littlegirl", "madam", "oldlady" };

	private static final Map<String, Person> map = new HashMap<String, Person>();

	static {
		map.put("littlegirl", new LittleGirl());
		map.put("madam", new Madam());
		map.put("oldlady", new OldLady());
	}

	public static void main(String[] args) throws ClassNotFoundException {
		Person person = new Person();
		person.setAge(Age.YOUNG);
		person.setSex(Sex.FEMALE);

		String key = choose(person);
		Person p = map.get(key);
		p.print();

	}

	private static String choose(Person person) {
		String key = null;
		Sex sex = person.getSex();
		Age age = person.getAge();
		int orderInTable = 0;
		for (Sex t : Sex.values()) {
			for (Age s : Age.values()) {
				if (s.equals(sex) && t.equals(age)) {
					return table[orderInTable];
				}
				orderInTable++;
			}
		}
		return key;
	}
}</span>

这里的驱动表需要Sex和Age的顺序想对应,即:

Sex :  1 FEMALE  2MALE

Age :  1 YOUNG  2 ADULT  3 OLD

则table的第一需要是littlegirl,第二个需要是madam,第三个是oldlady,也和choose函数的循环有关,是Sex在外层,Age在内层,才会出现这样的顺序。










版权声明:本文为sanniao原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。