学习设计原则之前先了解java OO基础:
- 抽象
- 封装
- 多态(重点之重)
- 继承
几个重要面向对象设计原则:
- 1.面向接口(超类)编程
- 2.少用继承,多用接口
- 3.封装变化(将变化的部分封装起来)
一. 一步步开始设计类图
针对鸭子 我们设计类结构及之间的关系。
按照一般的想法,我们会设计一个超类Duck,方法有quack(),swim()等,其他子类鸭子统统继承这个超类Duck,这样子类就不用重新写相关方法,增加了代码复用性。类图如下:

此时如果想增加一个行为fly,把他添加到Duck超类,那么子类同样都可以继承。但是如果有一只玩具鸭他不会飞,那么在超累Duck中添加fly方法,就会有问题了,玩具鸭不该具有的飞行fly行为,却被玩具鸭从Duck类继承了!
你又想到,可以使用继承然后覆盖重写超类的方法,玩具鸭重写fly方法,变成不会飞,这样确实可行!但如果有一只鸭子不会飞,但又会瓜瓜叫,又有一只鸭子会飞但不会瓜瓜叫…如果有类似的鸭子n只,岂不是每个类都需要重写覆盖一些方法么?可见使用继承Duck,这种方式有一些死板,超类Duck的行为,继承的子类不一定都具有Duck的行为,而且代码在子类中都会有重复,且子类鸭子的行为不能轻易改变。
我们说到面向接口编程,少用继承,把变化的部分拿出来封装。那么我们把可能会发生变化的行为 Fly行为和Quak叫的行为都从Duck超类拿出来,形成两个接口。使各种各样的fly的行为,如FlyWithWings,FlyNoWay,或者叫的行为,如Quack方式叫,Squeak方式叫,去实现这两个接口。

这样一来,飞行的行为和叫的行为被单独拿出来了,已经与Duck类无关,弹性增加。
上方已经把FlyBeahvior和QuackBeahvior从Duck超类中剥离,那么怎样让Duck类使用到这两个行为呢?采用委托代理的方式,在Duck类中添加这两个接口,通过这两个接口执行具体的fly行为或quack行为。Duck类如图:
子类鸭子如何设定Flybehavior和QuackBehavior的实例呢?
通过构造的方式,来实例化具体的行为(这种方式有些死板,因为具体的行为是静态构造的,稍后我们通过其他方法使之能动态改变)
构造方式如下:

/*
这是一直会吱吱叫和会飞行的鸭子
*/
public class OneFlyDuck2 extends Duck {
public OneFlyDuck2() {
quackBehavior = new QuackWithSqueak(); //
flyBehavior = new FlyWithWings(); // 用翅膀飞行
}
}
二.代码编写:
2.1第一次编码实现:
Duck超类:
鸭子都有的固定的属性或者行为
*/
public class Duck {
FlyBehavior flyBehavior; //声明两个行为
QuackBehavior quackBehavior;
//都会游泳
public void swim(){
System.out.println("I can swim");
};// 游泳
public void performFlyBehavior(){
flyBehavior.fly(); //使用flyBeahvior接口代理执行飞的具体行为
}
public void performQuackBehavior(){
quackBehavior.quack();//使用QuackBehavior接口代理执行叫的具体行为
}
FlyBehavior:
/*
各种飞的接口
*/
public interface FlyBehavior{
void fly();//各种飞
}
QuackBehavior:
/*
叫的接口
*/
public interface QuackBehavior {
void quack();//叫
}
FlyWithWings实现FlyBehavior接口:
public class FlyWithWings implements FlyBehavior {
@Override
public void fly(){
System.out.println("I fly with Wings!");
}
}
FlyWithNoWay实现FlyBehavior接口:
public class FlyWithNoWay implements FlyBehavior {
@Override
public void fly(){
System.out.println("I fly with noWay!");
}
}
Squeak实现QuackBehavior接口:
/*
吱吱叫
*/
public class Squeak implements QuackBehavior{
@Override
public void quack(){
System.out.println("I can Sequack!"); }
}
Quack叫实现QuackBehavior接口:
/*
瓜瓜叫
*/
public class Quack implements QuackBehavior {
public void quack(){
System.out.println("I can Quack!");
}
}
一个子类鸭子:
/*
这是一直会吱吱叫和会飞行的鸭子
*/
public class OneFlyDuck2 extends Duck {
public OneFlyDuck2() {
quackBehavior = new Squeak(); //
flyBehavior = new FlyWithWings(); // 用翅膀飞行
}
}
测试类:
public static void main(String[] args) {
Duck oneflyDuck2=new OneFlyDuck2();
FlyBehavior flyBehavior=new FlyWithWings(); //翅膀飞行
QuackBehavior quackBehavior1=new Squeak(); //吱吱方式叫
oneflyDuck2.performFlyBehavior();; //这种事oneFlyDuck 构造方式创建的
oneflyDuck2.performQuackBehavior();
}
结果:
2.2 第二次编码改进
上方是通过子类构造器方式实例化鸭子具有的行为,这样比较僵硬,有一种方式可以动态地set设定鸭子的行为,方式如下:**
在Duck类中新增set方法,设定具体的飞的行为or叫的行为,然后子类中不需要构造了。
新Duck类:
/*
鸭子都有的固定的属性或者行为
*/
public class Duck {
FlyBehavior flyBehavior; //声明两个行为
QuackBehavior quackBehavior;
//都会游泳
public void swim(){
System.out.println("I can swim");
};// 游泳
public void performFlyBehavior(){
flyBehavior.fly();
}
public void performQuackBehavior(){
quackBehavior.quack();
}
//新增的方法
public void setFlyBehavior(FlyBehavior flyBehavior1){
this.flyBehavior=flyBehavior1;
}
public void setQuackBehavior(QuackBehavior quackBehavior1){
this.quackBehavior=quackBehavior1;
}
}
新子类:
/*
这是某一种鸭子
*/
public class OneFlyDuck extends Duck{
}
测试类:
public static void main(String[] args) {
Duck oneflyDuck=new OneFlyDuck();
FlyBehavior flyBehavior=new FlyWithWings(); //翅膀飞行 父类接口指向子类引用
QuackBehavior quackBehavior1=new Squeak(); //吱吱方式叫 父类接口指向子类引用
oneflyDuck.setFlyBehavior(flyBehavior);//传递会飞的行为
oneflyDuck.setQuackBehavior(quackBehavior1); //传递会叫的行为。
oneflyDuck.performFlyBehavior();//执行飞的行为
oneflyDuck.performQuackBehavior();//执行叫的行为}
结果:
2.3额外补充
如果新增一只鸭子,他是用火箭实现飞行的,那么只需要新建一个火箭飞行类实现FlyBehavior接口,而不需要修改原有代码.
/*
新增加的飞行方式
*/
public class RocketFly implements FlyBehavior{
@Override
public void fly(){
System.out.println("I can fly with Rocket!!");
}
}
测试:
public static void main(String[] args) {
Duck duckRocket=new OneRocketDuck();
FlyBehavior flyBehavior2=new RocketFly(); //实例化 使用火箭飞的行为
duckRocket.setFlyBehavior(flyBehavior2);//设定飞的行为 是通过火箭方式
duckRocket.performFlyBehavior();//执行fly
}
代码的执行过程:
duckRocket是指向子类的父类引用,因为多态特性,因此duckRocket可以调用在Duck类中的setFlyBehavior方法。
flyBehavior是指向子类的父接口引用,将flybehavior作为参数传入setFlyBehavior方法,就是实例化了Duck类中的flyBehavior。
最后使用duckRocket调用performFlyBehavior方法,就是执行了flyBehavior.fly(),因为多态,且已经第二步实例化了flyBehavior,因此调用的就是RocketFly中的fly方法。
结果:
三.最终类设计图

以上就是策略模式:
此模式将变化的部分独立出来封装。