天天看点

【Java设计模式】1.策略模式写在文章的最前面什么是设计模式?问题的引出具体实现代码以及分析总结及分析

【java设计模式】1.策略模式

  • 写在文章的最前面
  • 什么是设计模式?
  • 问题的引出
    • 第一步,也即最开始的想法
    • 策略模式的引出
  • 具体实现代码以及分析
    • 1.Duck抽象类
    • 2.FlyBehavior以及QuackBehavior接口
      • FlyBehavior
      • QuackBehavior
    • 3.FlyWithWings以及FlyWithoutWings
      • FlyWithWings
      • FlyWithoutWings
    • 4.Quack以及CantQuack
      • Quack
      • CantQuack
    • 5.normalduck以及modelduck(环境角色)
      • normalduck
      • modelduck
  • 总结及分析

写在文章的最前面

本人是java小白,自学没多久,在之前学校的课程中学习了java,并对java产生了一定的兴趣,不过只学习了一些最基本的语法。课上老师给我们提了一个题目,让我们自己去写,当时是写售货机的系统,参考了很多才勉强写出来。后来经同学推荐,开始看设计模式的书,所以想以写博客的方式来激励自己学习,并作一些记录。本人典型理工科 diao丝,所以文笔不好,也借此机会锻炼锻炼自己的文笔,有写的语言不通和错误的地方,请大佬勿喷,同为初学者的大家一起交流交流,私信我,再次感激不尽!!

什么是设计模式?

设计模式即为java项目设计中的一种设计方法,通过这些前辈总结的方法以及设计思想,可以让我们在实际java项目开发的时候有规律可循,通过对于具体项目的解剖以及思考,使我们可以对应使用相应的设计模式,大大简化了设计难度,使用科学有效的设计模式,能少走一些不必要的弯路。

问题的引出

现在,我们想要做出一个 “鸭子” 产品,这个鸭子产品拥有下面几个 属性 或 功能 :

  1. 这个 “鸭子” 它会叫,并且不同的鸭子叫声是不一样的;
  2. 这个 “鸭子” 它会飞,而且有的鸭子会飞,有的鸭子飞不起来,飞不起来的原因有很多,比如翅膀坏了,又或是这个 “鸭子” 根本没有翅膀,最简单的例子:洗澡的橡皮鸭,对吧!它确实是没有翅膀;
  3. 这个 “鸭子” 它会呈现给我们一个信息,就像一个按钮,当你按下去的时候,它会告诉你一些东西,可以是关于它是什么鸭子,或者它是否能飞,当然这取决于你,对,就是程序员或者…你的老板(微笑中透露出了些许无奈,毕竟要恰饭的 ) 。

第一步,也即最开始的想法

首先创建一个Duck类,这个Duck类包含了一些属性,比如重量、品种、毛皮颜色等,这里可以设置几个以protected为访问控制符的成员变量,变量名称用对应英文即可,例如Weight、Variety、FurColor。然后就是为Duck这个类创建方法,方法包含了叫、飞以及"自我介绍"的动作。

但是,迎面而来的问题就是,不同的 “鸭子” 拥有不同的属性、行为(即方法),这样当你想创建多个不同鸭子的时候,则要创建对应数量的类。这样就完全没有运用到代码的复用,不仅复杂而且低效。所以对于代码复用,像我一样的初学者一般想到的就是继承。个人认为 继承 这个机制有利也有弊,当继承过于复杂时,继承的缺点就暴露出来了,最大的问题就是一个类无法继承两个即两个以上的父类,这就限制了继承的使用。

那么我们先来使用继承父类来试试,首先写一个Duck类,把各种属性以及方法写好,而子类继承父类后,如果子类未进行重写,则方法以及属性仍然默认 “运用” 父类的方法以及属性。但是我们要编写的是好几个属性以及方法可能不同的 “鸭子” 类。也就意味着代码仍然无法很好的服用,假如多个类之间几乎没有相同的属性以及方法,那继承的属性以及方法基本都是无用的,都要重写,这样代码复用仍然没有很好的实现。

同时,如果这时候,你的老板或者策划突然萌生了一个别的想法,给这些 “鸭子” 加一个动作,比如下蛋,或者迁徙。这个时候如果要加这样一个方法,那么Duck父类以及下面所有的子类几乎都要手动添加这样一个方法,明显很难实现,在具体工程项目的实践过程中,这样低效率的代码在面临反复多次的修改时会显得很无力,几乎每次修改添加都是重写,而且要重写很多次。

策略模式的引出

因此要解决这样一个问题,即我们要编写一群类,这些类都有一些共通的属性或者方法,这些属性或方法在大的方面属于一类,但每一类都或多或少有些区别,就比如鸭子的叫声有正常鸭子的"呱呱呱"的叫也有橡皮鸭的"叽叽叽"的叫,这都属于发出叫声这样一个行为,但又有所不同,在这种情况下,引用比较专业的说法,就是将这样一种模式细分为下面三种组成:

  1. 抽象策略角色这个是一个抽象的角色,通常情况下使用接口或者抽象类去实现。对比来说,就是我们的下面代码例子中的FlyBehavior以及QuackBehavior接口。
  2. 具体策略角色包装了具体的算法和行为。对比来说,就是实现了FlyBehavior以及QuackBehavior接口的实现一组实现类。
  3. 环境角色内部会持有一个抽象角色的引用,给客户端调用。对比来说,就是我们的具体 “鸭子” 类别。说明:具体的Duck类内部一定会有一个策略类的一个成员变量,这样做的目的在于可以当我们在去创建具体的Duck对象如NormalDuck的时候,可以接收我们向NormalDuck类中传递的具体的策略类。

具体实现代码以及分析

1.Duck抽象类

public abstract class Duck {
	FlyBehavior flybehavior;
	QuackBehavior quackbehavior;
	
	public abstract void display();
	
	public void performFly(){
		flybehavior.fly();
	}
	
	public void performQuack(){
		quackbehavior.quack();
	}
	
	public Duck(){
		
	}
	
	public void setFly(FlyBehavior fb){
		flybehavior = fb;
	}
	
//	public void setDuckWithWings(){
//		flybehavior = new FlyWithWings();
//	}
	
//	public void setDuckWithoutWings(){
//		flybehavior = new FlyWithoutWings();
//	}
	
	public void setQuack(QuackBehavior qk){
		quackbehavior = qk;
	}

//	public void setDuckCanQuack(){
//		quackbehavior = new Quack();
//	}
	
//	public void setDuckCantQuack(){
//		quackbehavior = new CantQuack();
//	}
}
           

Duck抽象类中包含了一个抽象的方法display(),用来让鸭子 “声明” 自己的一些信息,或者其他功能,而所有继承Duck抽象类的具体duck类都必须要重写此抽象方法。同时申明了两个抽象策略角色,即flybehavior以及quackbehavior,在后续实例化时,再对这两个变量进行具体的实例化。具体下面代码会解释。

2.FlyBehavior以及QuackBehavior接口

FlyBehavior

public interface FlyBehavior {
	public void fly();
}
           

QuackBehavior

public interface QuackBehavior {
	public void quack();
}
           

3.FlyWithWings以及FlyWithoutWings

FlyWithWings

public class FlyWithWings implements FlyBehavior {
	public void fly(){
		System.out.println("I am flying!");
	}
}
           

FlyWithoutWings

public class FlyWithoutWings implements FlyBehavior {
	public void fly(){
		System.out.println("I can't fly!");
	}
}
           

4.Quack以及CantQuack

Quack

public class Quack implements QuackBehavior {
	public void quack(){
		System.out.println("Gua Gua");
	}
}
           

CantQuack

public class CantQuack implements QuackBehavior {
	public void quack(){
		System.out.println("<<Slience>>");
	}
}
           

5.normalduck以及modelduck(环境角色)

normalduck

public class normalduck extends Duck {
	public normalduck(){
		flybehavior = new FlyWithWings();
		quackbehavior = new Quack();
	}	
	
	public void display(){
		System.out.println("I am a normal duck");
	}

}
           

modelduck

public class modelduck extends Duck {
	public modelduck(){
		flybehavior = new FlyWithoutWings();
		quackbehavior = new Quack();
	}
	
	public void display() {
		System.out.println("I am a model duck");
	}
}
           

以上两个就是环境角色,一个是普通鸭子,另一个是模型鸭子。

总结及分析

  1. 可以看到,每种不同的鸭子类都可以直接使用已有的一些方法,我们需要做的就是选择当前鸭子类所包含的方法(即行为),当我们想改变这个鸭子类的一种方法,我们只需要将其改为我们提前设定好的一种方法,而不是将代码返工重写,提高了复用率。
  2. 如果我们想增加一种另一类的方法,比如给鸭子类增加一个 “迁徙” 行为(方法),则我们可以再写接口,即 “迁徙” 接口,然后再对迁徙这个行为具体的实现方式进行实现,比如向南迁徙或向北迁徙,这些"行为类"实现了 “迁徙” 接口。这样我们就可以很方便的给当前已经写好的环境角色类添加行为(方法)