需求
以动物为例,随便举几个例子,鸡、鸭、鱼、猫、狗。
简单看一下动物的动作
- 鸡
public class Chicken {
public void drinkWater(){
System.out.println("喝水");
}
public void voice(){
System.out.println("咯咯!");
}
public void walk(){
System.out.println("两条腿走路");
}
public void fly(){
System.out.println("挥动翅膀飞");
}
}
- 鸭
public class Duck {
public void drinkWater(){
System.out.println("喝水");
}
public void voice(){
System.out.println("嘎嘎!");
}
public void walk(){
System.out.println("两条腿走路");
}
public void fly(){
System.out.println("挥动翅膀飞");
}
}
- 鱼
public class Fish {
public void drinkWater(){
System.out.println("喝水");
}
public void swim(){
System.out.println("游泳!");
}
}
- 猫
public class Cat {
public void drinkWater(){
System.out.println("喝水");
}
public void voice(){
System.out.println("喵喵!");
}
public void walk(){
System.out.println("四条腿走路");
}
}
- 狗
public class Dog {
public void drinkWater(){
System.out.println("喝水");
}
public void voice(){
System.out.println("汪汪!");
}
public void walk(){
System.out.println("四条腿走路");
}
}
这个时候你就要说了,这样写了好多重复的代码,为什么不抽取一个父类呢。
两种抽取思路
- 抽取公共方法
public class Animal {
public void drinkWater() {
System.out.println("喝水");
}
}
这样抽取完成后,所有子类不需要写drinkWater这个方法了,但是鸡、鸭都要写两条腿走路和挥动翅膀飞的方法,猫和狗都要写四条腿走路的方法,而且这种不变的部分并没有得到封装。
- 所有方法放到父类:
public class Animal {
public void drinkWater() {
System.out.println("喝水");
}
public void chickenVoice(){
System.out.println("咯咯!");
}
public void walk2(){
System.out.println("两条腿走路");
}
public void fly(){
System.out.println("挥动翅膀飞");
}
public void duckVoice(){
System.out.println("嘎嘎!");
}
public void swim(){
System.out.println("游泳!");
}
public void catVoice(){
System.out.println("喵喵!");
}
public void walk4(){
System.out.println("四条腿走路");
}
public void voice(){
System.out.println("汪汪!");
}
}
这样抽取完成后,所有的动物都会继承得到各种方法,子类里不需要写任何方法(当然这是一种比较极端的情况)。但是你会发现鸡也可以四条腿走路,而且还会游泳,还会喵喵、嘎嘎、汪汪叫,鱼竟然会飞了。
策略模式
定义算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用的客户。
- 定义算法族,分别封装起来
以走路的接口为例
public interface IWalke {
void walk();
}
所有走路的动作都要实现这个接口,例如:
两条腿走路
public class WalkBy2Legs implements IWalke {
@Override
public void walk() {
System.out.println("两条腿走路");
}
}
四条腿走路
public class WalkBy4Legs implements IWalke {
@Override
public void walk() {
System.out.println("四条腿走路");
}
}
不会走路
public class WalkNoWay implements IWalke {
@Override
public void walk() {
}
}
- 让算法的变化独立于客户的使用
父类Animal
public class Animal {
protected IWalk iWalk;
public void drinkWater() {
System.out.println("喝水");
}
public void walk(){
iWalk.walk();
}
}
接着写鸡和猫,其余的类似
鸡(当然两条腿走路的鸭子也是这样写)
public class Chicken extends Animal{
public Chicken(){
iWalk = new WalkBy2Legs();
}
public void drinkWater(){
System.out.println("喝水");
}
...
}
猫(当然四条腿走路的狗也是这样写)
public class Cat extends Animal{
public Cat(){
iWalk = new WalkBy4Legs();
}
public void drinkWater(){
System.out.println("喝水");
}
...
}
鱼
public class Fish extends Animal{
public Fish(){
iWalk = new WalkNoWay();
}
public void drinkWater(){
System.out.println("喝水");
}
...
}
-
也许你刚看到这的时候并不会感觉代码简单了,的确,代码没有简单,反而复杂了,那么,这样写的好处在哪?
- 代码并没有在多个子类重复。
- 应用中可能需要变化之处独立了出来,没有和那些不需要变化的代码混在一起。正如我们封装的IWalk接口,可能变化的是走路的方式,不变的是Animal的walk方法,方便以后可以轻易的改动或者扩展此部分。
- 针对接口编程,而不是针对实现编程。最开始的实现方式每种动物的行为方式捆绑在动物本身,现在专门有一些动作类实现了动作接口,由这些动作类而不是动物本身实现动作,所以实际的实现并没有绑定死在各种动物本身。
- 通过给每个接口设置入口,我们可以在运行时动态地改变动物的行为方式。
结束语
赘述这么多,相信如果有过项目框架抽取经验的人会深有体会。可以先放到脑子里,试着应用到自己的项目之中,你才会发现它的精巧之处,去发现吧。
网友评论