方针:
根本把握23种规划办法的根本概念并能够写出相应的比如
内容:
学习内容 1.规划办法的概念解说 2.画出规划办法的类图 3.举例阐明
方案:
引荐: 1、 每天一个规划办法的学习 2、 隔一天就进行一个办法的复习 3、 顺利敲出相关规划办法的事例
规划办法
要点:规划办法是一种思维,它的办法是多变的,首要把握他的规划思维 目的:编写软件进程中,程序员面临着来自耦合性、内聚性以及可保护性,可扩展性、重用性、灵敏性等多方面的应战,规划办法是为了让程序(软件),具有更好 代码重用性(即:相同功用的代码,不必屡次编写) 可读性(即:编程标准性,便于其他程序员的阅览) 可扩展性(即:当需求添加新的功用的时分,十分的便利,称为可保护) 可靠性(即:当咱们添加新的功用时,十分的便利,称为可保护)
规划办法的七个准则:单一责任、接口阻隔、依靠倒转、开闭准则、里式替换、迪米特规律、组成复用准则。 单一责任:对类来说,即一个类应该只担任一项责任,如类A担任两个不同责任:责任1、责任2.当责任1需求变更而改动A时,或许形成责任2履行错误,所以需求将类A的粒度分为A1,A2。 接口阻隔准则:客户端不应该依靠它不需求的接口,即一个类对另一个类的依靠应该建立在最小的接口上。 依靠倒转:中心思维便是面向接口编程 里式替换:在运用承继时,遵循里式替换准则,在子类中尽量不要重写父类的办法 开闭准则:一个软件实体,如类、模板和函数应该对扩展敞开,对修正关闭。用笼统构建结构,用完结扩展细节。 迪米特规律:一个方针应该对其他方针坚持最小的了解 组成复用准则:尽量运用组成或许聚合的办法,而不是承继。
-============================================================= 规划办法分为三品种型,共23种 * 创立型办法:单例办法、笼统工厂办法、原型办法、制作者办法、工厂办法办法。 * 结构型办法:适配器办法、桥接办法、装修办法、组合办法、外观办法、享元办法、署理办法。 * 行为性办法:模板办法、指令办法、拜访者办法、迭代器办法、调查者办法、中介者办法、备忘录办法、解说器办法、状况办法、战略办法、责任链办法
本文的部分图来历网络,如有侵权,请与我联络!
1.单例办法
保证一个类只需一个实例,并供给该实例的大局拜访点。
单例办法的完结有许多种,常见的有:饿汉式、懒汉式、静态内部类、DCL、枚举。下面咱们就从这种完结办法来了解学习单例办法
- 饿汉式
(饿汉式)优缺陷阐明:
- 长处:这种写法比较简略,便是在类装载的时分就完结实例化。防止了线程同步问题
- 缺陷:在类装载的时分就完结实例化,没有达到Lazy Loading(懒加载)的效果。假如从始至终从未运用过这个实例,则会形成内存的糟蹋。
- 这种办法基于classloader机制防止了多线程的同步问题,不过instance在类装载时就实例化,在单例办法中大多数都是调用getinstance办法,可是导致类装载的原因有许多种,因而不能确认有其他的办法(或许其他的静态办法)导致类装载,这时分初始化instance就没有达到lazy loading的效果。
- 定论:这种单例办法可用,或许形成内存糟蹋。
完结代码:
1.第一种完结办法
private final static SingletonModeDemo1 MysingletonMode = new SingletonModeDemo1();
public static SingletonModeDemo1 getMysingletonMode() {
return MysingletonMode;
}
2.第二种完结办法
//这种办法和上面的完结办法相同,仅仅将静态成员变量放到了静态代码块中创立。
private static SingletonModeDemo1 MysingletonMode1 = null;
{
MysingletonMode1 = new SingletonModeDemo1();
}
public static SingletonModeDemo1 getInstance(){
return MysingletonMode1;
}
- 懒汉式
(懒汉式–线程不安全)优缺陷阐明:
-
长处::起到了LazyLoading 的效果,可是只能在单线程下运用。
-
缺陷:假如在多线程下,一个线程进入了if(singleton ==null)判别句子块,还来得及往下履行,另一个线程也经过了这个判别句子,这时便会产生多个实例,所在在多线程环境下不可运用这种办法。
-
定论:在实践开发中,不要运用这种办法。
-
完结代码:
private static SingletonModeDemo1 singletonModeDemo1;
public static SingletonModeDemo1 getInstance(){
if (singletonModeDemo1==null){
singletonModeDemo1 = new SingletonModeDemo1();
}
return singletonModeDemo1;
}
- 懒汉式
(懒汉式–线程安全)优缺陷阐明:
- 长处::处理了线程安全问题
- 缺陷:功率太低了,每个线程在想取得类的实例时分,履行getinstance()办法都要进行同步,而其实这个办法只履行一个实例化代码就够了,后边的想取得该类实例,直接return就行了,办法进行同步功率太低
- 定论:在实践开发中,不引荐运用这种单例办法
完结代码:
private static SingletonModeDemo1 singletonModeDemo1;
public synchronized static SingletonModeDemo1 getInstance(){
if (singletonModeDemo1 == null) {
singletonModeDemo1 = new SingletonModeDemo1();
}
return singletonModeDemo1;
}
- 静态内部类
- 长处:防止了线程不安全,运用静态内部类特色完结推迟加载,功率高。
- 缺陷:相比较上面的几种完结办法,根本没有什么缺陷
- 定论:引荐运用。 完结代码:
private static class Instance{
public static SingletonModeDemo1 singletonModeDemo1= new SingletonModeDemo1();
}
public static SingletonModeDemo1 getInstance(){
return Instance.singletonModeDemo1;
}
- DCL
- 长处:Double-Check概念是多线程开发中常运用到的,如代码中所示,咱们进行了两次if(singleton == null)查看,这样就能够保证线程安全了。这样,实例化代码只用履行一次,后边再次拜访时,判别if(singleton ==null),直接return实例化方针,也防止的反复进行办法同步。
- 线程安全,推迟加载;功率较高
- 缺陷:相比较上面的几种完结办法,根本没有什么缺陷
- 定论:引荐运用。 完结代码:
private volatile static SingletonModeDemo1 singletonMode=null;
//不加volatile或许会呈现指令重排序,导致其他线程获取的这个方针是一个没有new完全的方针
public static SingletonModeDemo1 getInstance() {
if (singletonMode == null) {
synchronized (SingletonModeDemo1.class) {
if (singletonMode == null) {
singletonMode = new SingletonModeDemo1();
}
}
}
return singletonMode;
}
- 枚举
- 长处::凭借JDK1.5中添加的枚举来完结单例办法,不仅能防止多线程同步问题,而且还能防止反序列化从头创立新的方针。
- 缺陷:相比较上面的几种完结办法,根本没有什么缺陷
- 定论:引荐运用。
完结代码:
public enum Singleton{
INSTANCE;
public void doSomething(){
System.out.println("枚举");
}
}
public static void main(String[] args) {
// SingletonModeDemo1 mysingletonMode = SingletonModeDemo1.getMysingletonMode();
// SingletonModeDemo1 mysingletonMode1 = SingletonModeDemo1.getMysingletonMode();
// System.out.println(mysingletonMode == mysingletonMode1?"持平":"不持平");
Singleton.INSTANCE.doSomething();
}
}
单例办法的相关总结
- 单例办法留意事项和细节阐明 单例办法保证了体系内存中该类只存在一个方针,节省了体系资源,关于一些需求频频创立毁掉的方针,用单例办法能够进步体系功能。 当想实例化一个单例类的时分,必需求记住运用相应的获取方针的办法,而不是运用new
- 单例办法运用的场景:需求频频的进行创立和毁掉的方针,创立方针时耗时过多或耗费资源过多(即:重量级方针),但又常常用到的方针,工具类方针、频频拜访数据库或文件的方针(bs数据源、session工厂)
2.工厂办法(Factory)
工厂办法一共有三种办法:简略工厂办法、工厂办法办法、笼统工厂办法。其间简略工厂办法没有呈现在23种规划办法中,或许是由于太简略,而且这种办法在开发中根本用不到,所以没把它纳入傍边。
2.1.简略工厂办法
在创立一个方针时不向客户暴露内部细节,并供给一个创立方针的通用接口。
简略工厂把实例化的操作独自放到一个类中,这个类就成为简略工厂类,让简略工厂类来决议应该用哪个详细子类来实例化。
这样做能把客户类和详细子类的完结解耦,客户类不再需求知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,假如不运用简略工厂,那么一切的客户类都要知道一切子类的细节。而且一旦子类产生改动,例如添加子类,那么一切的客户类都要进行修正
- 简略工厂办法是归于创立型办法,是工厂办法的一种,简略工厂办法是由一个工厂方针决议创立出哪一种产品类的实例。简略工厂办法是工厂办法宗族中最简略有用的办法
- 简略工厂办法:界说了一个创立方针的类,由这个类来封装实例化方针的行为(代码)
- 在软件开发中,当咱们会用到许多的创立某种、某类或许某批方针时,就会运用到工厂办法。
事例类图:
代码完结:
public interface MoveAble {
void move();
}
public class Aircraft implements MoveAble{
@Override
public void move() {
System.out.println("飞机在飞...");
}
}
public class Car implements MoveAble {
@Override
public void move() {
System.out.println("车在跑...");
}
}
public class Ship implements MoveAble {
@Override
public void move() {
System.out.println("船在水上跑...");
}
}
public class VehicleFactory {
public Car createCar(){
return new Car();
}
public Aircraft createAircraft(){
return new Aircraft();
}
public Ship createShip(){
return new Ship();
}
}
public class Client {
public static void main(String[] args) {
VehicleFactory vehicleFactory = new VehicleFactory();
Car car = vehicleFactory.createCar();
car.move();
}
}
2.2.工厂办法
界说了一个创立方针的接口,但由子类决议要实例化哪个类。工厂办法把实例化操作推迟到子类。
在简略工厂中,创立方针的是另一个类,而在工厂办法中,是由子类来创立方针。
下图中,Factory 有一个 doSomething() 办法,这个办法需求用到一个产品方针,这个产品方针由 factoryMethod() 办法创立。该办法是笼统的,需求由子类去完结。
实例类图:
代码完结:
public class Client {
public static void main(String[] args) {
Factory aircraftFactory = new AircraftFactory();
aircraftFactory.createVehicle();
}
}
public abstract class Factory {
public abstract MoveAble getVehicle();
public MoveAble createVehicle(){
return getVehicle();
}
}
public interface MoveAble {
void move();
}
public class Aircraft implements MoveAble {
@Override
public void move() {
//TODO
}
}
public class AircraftFactory extends Factory {
@Override
public MoveAble getVehicle() {
return new Aircraft();
}
}
public class Car implements MoveAble {
@Override
public void move() {
//TODO
}
}
public class CarFactory extends Factory{
@Override
public MoveAble getVehicle() {
return new Car();
}
}
public class Ship implements MoveAble {
@Override
public void move() {
//TODO
}
}
public class ShipFactory extends Factory {
@Override
public MoveAble getVehicle() {
return new Ship();
}
}
2.3笼统工厂
供给一个接口,用于创立 相关的方针宗族 。
笼统工厂办法创立的是方针宗族,也便是许多方针而不是一个方针,而且这些方针是相关的,也便是说有必要一同创立出来。而工厂办法办法仅仅用于创立一个方针,这和笼统工厂办法有很大不同。
笼统工厂办法用到了工厂办法办法来创立单一方针,AbstractFactory 中的 createProductA() 和 createProductB() 办法都是让子类来完结,这两个办法独自来看便是在创立一个方针,这契合工厂办法办法的界说。
至于创立方针的宗族这一概念是在 Client 表现,Client 要经过 AbstractFactory 一同调用两个办法来创立出两个方针,在这儿这两个方针就有很大的相关性,Client 需求一同创立出这两个方针。
从高层次来看,笼统工厂运用了组合,即 Cilent 组合了 AbstractFactory,而工厂办法办法运用了承继。
实例类图: 实例代码:
public abstract class Pizza {
protected abstract void prepare();
protected abstract void bake();
protected abstract void cut();
protected abstract void box();
}
public abstract class PizzaFactory {
public abstract Pizza createPizza(String PizzaType);
}
public class BJPizzaFactory extends PizzaFactory{
@Override
public Pizza createPizza(String PizzaType) {
Pizza pizza = null;
if (PizzaType.equals("CheesePizza")){
pizza = new BJCheesePizza();
}else if(PizzaType.equals("GreekPizza")){
pizza = new BJGreekPizza();
}else {
return null;
}
return pizza;
}
}
public class HNPizzaFactory extends PizzaFactory {
@Override
public Pizza createPizza(String PizzaType) {
Pizza pizza = null;
if (PizzaType.equals("CheesePizza")){
pizza = new HNCheesePizza();
}else if(PizzaType.equals("GreekPizza")){
pizza = new HNGreekPizza();
}else {
return null;
}
return pizza;
}
}
public class BJGreekPizza extends Pizza {
String PizzaType;
public BJGreekPizza(){
this.PizzaType = "BJGreekPizza";
}
@Override
protected void prepare() {
System.out.println(PizzaType+"资料预备结束");
}
@Override
protected void bake() {
System.out.println(PizzaType+"正在烘烤");
}
@Override
protected void cut() {
System.out.println(PizzaType+"正在切片");
}
@Override
protected void box() {
System.out.println(PizzaType+"打包结束");
}
}
public class BJCheesePizza extends Pizza {
String PizzaType;
public BJCheesePizza(){
this.PizzaType = "BJCheesePizza";
}
@Override
protected void prepare() {
this.PizzaType = PizzaType;
System.out.println(PizzaType+"资料预备结束");
}
@Override
protected void bake() {
System.out.println(PizzaType+"正在烘烤");
}
@Override
protected void cut() {
System.out.println(PizzaType+"正在切片");
}
@Override
protected void box() {
System.out.println(PizzaType+"打包结束");
}
}
public class HNCheesePizza extends Pizza {
String PizzaType;
public HNCheesePizza(){
this.PizzaType = "HNCheesePizza";
}
@Override
protected void prepare() {
this.PizzaType = PizzaType;
System.out.println(PizzaType+"资料预备结束");
}
@Override
protected void bake() {
System.out.println(PizzaType+"正在烘烤");
}
@Override
protected void cut() {
System.out.println(PizzaType+"正在切片");
}
@Override
protected void box() {
System.out.println(PizzaType+"打包结束");
}
}
public class HNGreekPizza extends Pizza {
String PizzaType;
public HNGreekPizza(){
this.PizzaType = "HNGreekPizza";
}
@Override
protected void prepare() {
System.out.println(PizzaType+"资料预备结束");
}
@Override
protected void bake() {
System.out.println(PizzaType+"正在烘烤");
}
@Override
protected void cut() {
System.out.println(PizzaType+"正在切片");
}
@Override
protected void box() {
System.out.println(PizzaType+"打包结束");
}
}
public class PizzaOrder {
public void sellPizza() {
System.out.println("=============欢迎光临Disaster的Pizza店=====================");
System.out.println("您想买什么区域披萨:");
PizzaFactory factory = createFactory();
if (factory!=null){
System.out.println("挑选结束,请您输入想吃的披萨品种:");
}
while (true) {
if (factory==null){
break;
}
Pizza pizza = factory.createPizza(getPizzaType());
if (pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
System.out.println("请在输入你想买的其他Pizza:");
} else {
System.out.println("本店没有此披萨");
return;
}
}
}
private PizzaFactory createFactory() {
PizzaFactory pizzaFactory = null;
Scanner scanner = new Scanner(System.in);
String next = scanner.next();
if (next.equals("BJ")) {
pizzaFactory = new BJPizzaFactory();
} else if (next.equals("HN")) {
pizzaFactory = new HNPizzaFactory();
} else {
System.out.println("没有此区域的披萨");
return null;
}
return pizzaFactory;
}
private String getPizzaType() {
String stringBuilder = "";
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
try {
if ((stringBuilder = bf.readLine()) != null) {
return stringBuilder;
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
public class PizzaStore {
public static void main(String[] args) {
PizzaOrder pizzaOrder = new PizzaOrder();
pizzaOrder.sellPizza();
}
}
3.原型办法(Prototype)
原型办法:运用原型实例指定要创立方针的类型,经过仿制这个原型来创立新方针。相似于实际中的克隆概念。
在进行原型办法之前,咱们要先了解两个概念: 1.浅仿制(shallow copy)
- 关于数据类型是根本数据类型的成员变量,浅仿制会直接进行值传递,也便是将该特点值赋值一份给新的方针。
- 关于数据类型是引证数据类型的成员变量,比方说成员变量是某个数组,某个类的方针等, 那么浅仿制会进行引证传递,也便是仅仅将该成员变量的引证值(内存地址)赋一份给新的方针。由于实践上两个方针的该成员变量都指向同一个实例。在这种状况下,在一个方针中修正该成员变量会影响到另一个方针的该成员变量值。
- 浅仿制是运用默许的clone()办法完结的,也便是没有重写的Object中的Clone办法,它是同本地办法,是经过c++言语编写完结的相关方针的仿制操作的。 2.深仿制(deep copy)
- 仿制方针的一切根本数据类型的成员变量值。
- 为一切引证数据类型的成员变量恳求存储空间,并仿制每个引证数据类型成员变量所引证的方针,直到该方针可达的一切方针。也便是说,方针进行深仿制要对整个方针进行仿制。
- 深仿制完结办法1:重写clone办法来完结深仿制
- 深仿制完结办法2:经过方针序列化完结深仿制(引荐,由于clone办法的底层是c或c++来编写的,咱们无法看到详细的操作逻辑,咱们在运用它的时分是需求十分慎重的,可是序列化是jdk给咱们供给的,能够看到序列化的代码履行,一旦呈现问题,程序员也能够快速的找到处理方案)
代码事例: (浅仿制便是创立方针然后调用clone办法就能够得到一个新的方针,相对比较简略,这儿就没有放代码了) 1.经过clone办法来完结深仿制
public class Person implements Cloneable{
private String name;
private String age;
public Son son;
public Person(String name, String age, Son son){
this(name, age);
this.son = son;
}
public Person(String name, String age) {
this.name = name;
this.age = age;
}
public Son getSon() {
return son;
}
public void setSon(Son son) {
this.son = son;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", son=" + son +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person person = null;//创立一个方针用于接收克隆之后的方针
person = (Person) super.clone();//经过调用Object办法中的clone办法得到浅仿制方针
person.son = (Son) person.son.clone();//将方针中的引证成员变量也经过调用重写的clone办法来进行仿制。
//经过上面这两部操作能够完结深仿制,可是当一个方针的引证数据类型变多的时分,代码量也就会相应的增多,这是十分繁琐的进程。
return person;
}
}
public class Son implements Cloneable{
private String name;
private int age;
public Son(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Son son = null;
son = (Son) super.clone();
return son;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Person disaster = new Person("disaster","20",new Son("son",10));
Person disaster1 = (Person) disaster.clone();
Person disaster2 = (Person) disaster.clone();
Person disaster3 = (Person) disaster.clone();
System.out.println("disaster:"+disaster.toString()+"===="+"hashcode:"+disaster.hashCode()+"====son===="+disaster.getSon().hashCode());
System.out.println("disaster1:"+disaster.toString()+"===="+"hashcode:"+disaster1.hashCode()+"====son==="+disaster1.getSon().hashCode());
System.out.println("disaster2:"+disaster.toString()+"===="+"hashcode:"+disaster2.hashCode()+"====son==="+disaster2.getSon().hashCode());
System.out.println("disaster3:"+disaster.toString()+"===="+"hashcode:"+disaster3.hashCode()+"====son==="+disaster3.getSon().hashCode());
}
}
2.经过序列化来完结深仿制
public class Person implements Serializable {
private String name;
private String age;
public Son son;
public Person(String name, String age, Son son){
this(name, age);
this.son = son;
}
public Person(String name, String age) {
this.name = name;
this.age = age;
}
public Son getSon() {
return son;
}
public void setSon(Son son) {
this.son = son;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", son=" + son +
'}';
}
public Object deepCopy(){
//创立流方针
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
Person person = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
person = (Person)ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
if(bos!=null&&oos!=null&&bis!=null&&ois!=null){
bos.close();
oos.close();
bis.close();
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return person;
}
}
public class Son implements Cloneable,Serializable {
private String name;
private int age;
public Son(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Person disaster = new Person("disaster","20",new Son("son",10));
Person disaster1 = (Person) disaster.deepCopy();
Person disaster2 = (Person) disaster.deepCopy();
Person disaster3 = (Person) disaster.deepCopy();
System.out.println("disaster:"+disaster.toString()+"===="+"hashcode:"+disaster.hashCode()+"====son===="+disaster.getSon().hashCode());
System.out.println("disaster:"+disaster.toString()+"===="+"hashcode:"+disaster1.hashCode()+"====son==="+disaster1.getSon().hashCode());
System.out.println("disaster:"+disaster.toString()+"===="+"hashcode:"+disaster2.hashCode()+"====son==="+disaster2.getSon().hashCode());
System.out.println("disaster:"+disaster.toString()+"===="+"hashcode:"+disaster3.hashCode()+"====son==="+disaster3.getSon().hashCode());
}
}
4.制作者办法(Builder)
制作者办法(Buidler Pattern)又名生成器办法,是一种方针构建办法。它能够将杂乱方针的制作进程笼统出来(笼统类别),使这个笼统进程的不同完结办法能够构造出不同表现的(特点)的方针。
制作者办法是一步一步创立一个杂乱的方针,它答运用户只经过指定杂乱方针的类型和内容就能够构建他们,用户不需求知道内部的详细构建细节。
- 制作者办法的留意事项和细节
- 客户端(运用程序)不必知道产品内部组成的细节,将产品自身与产品的创立进程解耦,使得相同的创立进程能够创立不同的产品方针。
- 每一个详细制作者都相对独立,而与其他的详细制作者无关,因而能够很便利地替换详细制作者或添加新的详细制作者,用户运用不同的详细制作者即可得到不同的产品方针。
- 能够更爱精细得操控产品的创立进程。使杂乱产品的创立进程分化在不同的办法中,使得创立进程愈加明晰,也更便利地运用程序来操控创立进程。
- 添加新的详细制作者无需修正原有类库的代码,指挥者类针对笼统制作者类编程,体系扩展便利,契合开闭准则
- 制作者办法所创立的产品一般具有较多的共同点,其组成部分相似,假如产品之间的差异性很大,则不合适运用制作者弄湿,因而其运用范围受到必定的限制。
事例类图:
代码实例:
public class Client {
public static void main(String[] args) {
ComputerDirector director=new ComputerDirector();//1
ComputerBuilder builder=new MacComputerBuilder("I5处理器","三星125");//2
director.makeComputer(builder);//3
Computer macComputer=builder.getComputer();//4
System.out.println("mac computer:"+macComputer.toString());
ComputerBuilder lenovoBuilder=new LenovoComputerBuilder("I7处理器","海力士222");
director.makeComputer(lenovoBuilder);
Computer lenovoComputer=lenovoBuilder.getComputer();
System.out.println("lenovo computer:"+lenovoComputer.toString());
}
}
public class Computer {
private String cpu;//有必要
private String ram;//有必要
private int usbCount;//可选
private String keyboard;//可选
private String display;//可选
public Computer(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
public void setUsbCount(int usbCount) {
this.usbCount = usbCount;
}
public void setKeyboard(String keyboard) {
this.keyboard = keyboard;
}
public void setDisplay(String display) {
this.display = display;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", ram='" + ram + '\'' +
", usbCount=" + usbCount +
", keyboard='" + keyboard + '\'' +
", display='" + display + '\'' +
'}';
}
}
public abstract class ComputerBuilder {
public abstract void setUsbCount();
public abstract void setKeyboard();
public abstract void setDisplay();
public abstract Computer getComputer();
}
public class LenovoComputerBuilder extends ComputerBuilder {
private Computer computer;
public LenovoComputerBuilder(String cpu, String ram) {
computer = new Computer(cpu, ram);
}
@Override
public void setUsbCount() {
computer.setUsbCount(4);
}
@Override
public void setKeyboard() {
computer.setKeyboard("联想键盘");
}
@Override
public void setDisplay() {
computer.setDisplay("联想显示器");
}
@Override
public Computer getComputer() {
return computer;
}
}
public class MacComputerBuilder extends ComputerBuilder {
private Computer computer;
public MacComputerBuilder(String cpu, String ram) {
computer = new Computer(cpu, ram);
}
@Override
public void setUsbCount() {
computer.setUsbCount(2);
}
@Override
public void setKeyboard() {
computer.setKeyboard("苹果键盘");
}
@Override
public void setDisplay() {
computer.setDisplay("苹果显示器");
}
@Override
public Computer getComputer() {
return computer;
}
}
public class ComputerDirector {
public void makeComputer(ComputerBuilder builder){
builder.setUsbCount();
builder.setDisplay();
builder.setKeyboard();
}
}
其实关于构造者办法还有一种写法,这种写法许多结构都有用,相比较上面的写法,下面的写法在实践开发中会运用的更多。
public class Computer {
private String cpu;//有必要
private String ram;//有必要
private int usbCount;//可选
private String keyboard;//可选
private String display;//可选
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.usbCount = builder.usbCount;
this.keyboard = builder.keyboard;
this.display = builder.display;
}
public static class Builder {
private String cpu;//有必要
private String ram;//有必要
private int usbCount;//可选
private String keyboard;//可选
private String display;//可选
public Builder(String cup, String ram) {
this.cpu = cup;
this.ram = ram;
}
public Builder setUsbCount(int usbCount) {
this.usbCount = usbCount;
return this;
}
public Builder setKeyboard(String keyboard) {
this.keyboard = keyboard;
return this;
}
public Builder setDisplay(String display) {
this.display = display;
return this;
}
public Computer build() {
return new Computer(this);
}
}
}
这种制作者办法的精妙之处就在于能够有效的处理类爆破的问题,而且在运用进程中愈加的便利。
总结: //TODO
5.适配器办法(Adapter)
将类的接口转化为客户所希望的另一个接口。适配器答应类一同作业,不然由于接口不兼容而无法作业 用朴素的话说:适配器办法答应在适配器中包装其他不兼容的方针,使其与另一个类兼容。 适配器的分类:(命名办法是根据src是以怎样的办法给到Adapter(在Adapter里的办法)来命名的)。 1.类适配器:以类给到,在Adapter里,便是将src当做列,承继
- Adapter类,经过承继src类,完结dst类接口,完结src->dst的适配
- 类适配器办法的留意事项和细节
- java是单承继 机制,所以类适配器需求src类这一点算是一个缺陷,由于这要求dst有必要是接口,有必定的局限性。
- src类的办法在Adapter中都会暴露出来,也添加了运用的本钱
- 由于其承继了src类,所以它能够根据需求重写src类办法,使得Adapter的灵敏性增强了。
2.方针适配器:以方针给到,在Adapter里,将src作为一个方针,持有
- 根本思路和类的适配器办法相同,仅仅将Adapter类作修正,不是承继src类,而是src类的实例,以处理兼容性的问题。即:持有src类,完结dst接口,完结src->dst的适配。
- 根据“组成复用准则”,在体系中尽量运用相相关系来代替承继联系。
- 方针适配器办法是适配器办法常用的一种
3.接口适配器:以接口给到,在Adapter里,将src作为一个接口,完结
- 中心思路:当不需求全部完结接口供给的办法时,可先规划一个笼统类完结接口,并为该接口中每个办法供给一个默许完结(空办法),那么该笼统类的子类可有挑选地掩盖父类的某些办法来完结需求。
- 适用于一个接口不想运用其一切的办法的状况
运用场景:
- 你想要运用一个存在的类,可是他的接口类型不是你所需求的
- 你希望创立一个可重用类,该类需求与不相关的类(即不必定具有兼容接口的类)合作。
- 您需求运用几个现有的子类,可是经过对每个子类进行子类调整它们的接口是不切实践的。方针适配器能够调整其父类的接口。
- 大多数运用第三方库的运用程序运用适配器作为运用程序和第三方库之间的中间层,以便将运用程序与库别离。假如有必要运用另一个库,则只需求一个新库的适配器,而不必更改运用程序代码。
类适配器和方针适配器的不同: 类适配器
- 经过提交详细的习惯性类,使习惯性习惯与方针。因而,当咱们想要调整一个类及其一切子类时,类适配器将无法作业。
- 需求咱们重写Adapter的一些行为,由于Adapter是Adapter的子类。 方针适配器
- 假如咱们运用多个Adapter–即适配器自身和它的一切子类(假如有的话)。适配还能够一次向一切适配器添加功用。
- 使得掩盖习惯性行为变得愈加困难。它需求对适配器进行子类化,并使Adapter引证子类而不是适配器自身。
事例类图:
事例代码:
public class Client {
public static void main(String[] args) {
Cat cat = new Cat();
new Dog(cat).dogBarking();
}
}
public interface Bark {
void dogBarking();
}
public interface Catch {
void catching();
}
public class Dog implements Bark{
private Catch aCatch;
public Dog(Catch aCatch){
this.aCatch = aCatch;
}
@Override
public void dogBarking() {
aCatch.catching();
}
}
public class Cat implements Catch {
@Override
public void catching() {
System.out.println("猫在抓老鼠...");
}
}
6.桥接办法(Bridge)
将笼统与完结别离开来,使它们能够独立改动。 传统处理一些问题的时分或许会有扩展性问题(类爆破),违背了单依责任准则,因而提出了桥接办法来处理这些问题。 桥接办法基于类的最小规划准则,经过运用封装、聚合及承继等行为让不同的类承当不同的责任。它的首要特色是把笼统(Abstract)与行为完结(Implement)别离开来,然后能够坚持各部分的独立性以及对应他们的功用扩展。
桥接办法的留意事项和细节
1.完结了笼统和事项部分的别离,然后极大的供给了体系的灵敏性,让笼统部分和完结部分独立开来,这有助于体系进行分层规划,然后产生更好的结构化体系 2.关于体系的高层部分,只需求知道笼统部分和完结部分的接口就能够了,其他的部分由详细的事务来完结 3.桥接办法代替多层承继方案,能够削减子类的个数,下降体系的办理和保护本钱。 4.桥接办法的引进添加了体系的了解和规划难度,由于聚合相相关系建立在笼统层,要求开发者针对笼统进行规划和编程。 5.桥接办法要求正确识别出体系中两个独立改动的唯独,因而运用范围有必定的局限性,即需求有这样的运用场景。
桥接办法的运用场景: 1.JDBC驱动程序 2.银行转账体系 音讯类型:即时音讯、延时音讯
桥接办法在JDBC的源码剖析: JDBC的Driver接口,假如从桥接办法来看,Driver便是一个接口,下面能够有MYSQL的Driver,Oracle的Driver,这些就一个当作完结接口类。 事例类图: 事例代码:
public class Client {
public static void main(String[] args) {
RemoteControl remoteControl1 = new ConcreteRemoteControl1(new RCA());
remoteControl1.on();
remoteControl1.off();
remoteControl1.tuneChannel();
RemoteControl remoteControl2 = new ConcreteRemoteControl2(new Sony());
remoteControl2.on();
remoteControl2.off();
remoteControl2.tuneChannel();
}
}
public interface TV {
void on();
void off();
void tuneChannel();
}
public abstract class RemoteControl {
protected TV tv;
public RemoteControl(TV tv){
this.tv = tv;
}
public abstract void on();
public abstract void off();
public abstract void tuneChannel();
}
public class Sony implements TV{
@Override
public void on() {
System.out.println("Sony.on()");
}
@Override
public void off() {
System.out.println("Sony.off()");
}
@Override
public void tuneChannel() {
System.out.println("Sony.tuneChannel()");
}
}
public class RCA implements TV {
@Override
public void on() {
System.out.println("RCA.on()");
}
@Override
public void off() {
System.out.println("RCA.off()");
}
@Override
public void tuneChannel() {
System.out.println("RCA.tuneChannel()");
}
}
public class ConcreteRemoteControl1 extends RemoteControl {
public ConcreteRemoteControl1(TV tv) {
super(tv);
}
@Override
public void on() {
System.out.println("ConcreteRemoteControl1.on()");
tv.on();
}
@Override
public void off() {
System.out.println("ConcreteRemoteControl1.off()");
tv.off();
}
@Override
public void tuneChannel() {
System.out.println("ConcreteRemoteControl1.tuneChannel()");
tv.tuneChannel();
}
}
public class ConcreteRemoteControl2 extends RemoteControl {
public ConcreteRemoteControl2(TV tv) {
super(tv);
}
@Override
public void on() {
System.out.println("ConcreteRemoteControl2.on()");
tv.on();
}
@Override
public void off() {
System.out.println("ConcreteRemoteControl2.off()");
tv.off();
}
@Override
public void tuneChannel() {
System.out.println("ConcreteRemoteControl2.tuneChannel()");
tv.tuneChannel();
}
}
7. 装修(Decorator)
为方针动态添加功用。 装修者(Decorator)和详细组件(ConcreteComponent)都承继自组件(Component),详细组件的办法完结不需求依靠于其它方针,而装修者组合了一个组件,这样它能够装修其它装修者或许详细组件。所谓装修,便是把这个装修者套在被装修者之上,然后动态扩展被装修者的功用。装修者的办法有一部分是自己的,这归于它的功用,然后调用被装修者的办法完结,然后也保留了被装修者的功用。能够看到,详细组件应当是装修层次的最低层,由于只需详细组件的办法完结不需求依靠于其它方针。
- 装修者办法:动态的将新功用附加到方针上。在方针功用扩展方面,它比承继更有弹性,装修者办法也表现了开闭准则(OCP)
- Java的IO结构,FilterInputStream便是一个装修者 事例类图: 事例代码:
public class Client {
public static void main(String[] args) {
Beverage beverage = new HouseBlend();
beverage = new Mocha(beverage);
beverage = new Milk(beverage);
System.out.println(beverage.cost());
}
}
public interface Beverage {
double cost();
}
public abstract class CondimentDecorator implements Beverage {
protected Beverage beverage;
}
public class DarkRoast implements Beverage{
@Override
public double cost() {
return 1;
}
}
public class HouseBlend implements Beverage {
@Override
public double cost() {
return 1;
}
}
public class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return 1 + beverage.cost();
}
}
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return 1 + beverage.cost();
}
}
8.组合办法(Composite)
将方针组组成树形结构来表明“全体/部分”层次联系,答运用户以相同的办法处理独自方针和组合方针。
- 首要用于处理树形结构的时分所用的一种办法
- 组合办法(Composite Pattern),又名部分全体办法,它创立了方针组的树形结构, 将方针组组成树状结构以表明“全体-部分”的层次联系。
- 组合办法根据树形结构来组合方针,用来表明部分以及全体层次。
- 这品种型的规划办法归于结构型办法。
- 组合办法使得用户对单个方针和组合方针的拜访具有共同性,即:组合能让客户以共同的办法处理个别方针以及组合方针
事例类图: 事例代码:
public class Client {
public static void main(String[] args) {
Composite root = new Composite("root");
Component node1 = new Leaf("1");
Component node2 = new Composite("2");
Component node3 = new Leaf("3");
root.add(node1);
root.add(node2);
root.add(node3);
Component node21 = new Leaf("21");
Component node22 = new Composite("22");
node2.add(node21);
node2.add(node22);
Component node221 = new Leaf("221");
node22.add(node221);
root.print();
}
}
public abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
public void print() {
print(0);
}
abstract void print(int level);
abstract public void add(Component component);
abstract public void remove(Component component);
}
public class Composite extends Component{
private List<Component> child;
public Composite(String name) {
super(name);
child = new ArrayList<>();
}
@Override
void print(int level) {
for (int i = 0; i < level; i++) {
System.out.print("--");
}
System.out.println("Composite:" + name);
for (Component component : child) {
component.print(level + 1);
}
}
@Override
public void add(Component component) {
child.add(component);
}
@Override
public void remove(Component component) {
child.remove(component);
}
}
public class Leaf extends Component{
public Leaf(String name) {
super(name);
}
@Override
void print(int level) {
for (int i = 0; i < level; i++) {
System.out.print("--");
}
System.out.println("left:" + name);
}
@Override
public void add(Component component) throws UnsupportedOperationException{
throw new UnsupportedOperationException(); // 献身透明性交换单一责任准则,这样就不必考虑是叶子节点仍是组合节点
}
@Override
public void remove(Component component) throws UnsupportedOperationException{
throw new UnsupportedOperationException();
}
}
9.外观办法(Fade)
供给了一个一致的接口,用来拜访子体系中的一群接口,然后让子体系更简略运用。 外观办法(Facade),也叫“进程办法”:外观办法为子体系中的一组接口供给一个共同的界面,此办法界说了一个高层接口,这个接口使得这一子体系愈加简略运用 外观办法经过界说一个共同的接口,用以屏蔽内部子体系的细节,使得调用端只需跟这个接口产生调用,而无需关怀这个子体系的内部细节。
外观办法的留意事项和细节:
外观办法对外屏蔽了子体系的细节,因而外观办法下降了客户端对子体系运用的杂乱性
- 外观办法对客户端与子体系的耦合联系,让字体系内部的模块更易保护和扩展
- 经过合理的运用外观办法,能够帮咱们更好的区分拜访的层次 当体系需求进行分层规划时,能够考虑运用Facade办法
- 当保护一个留传的大型体系时,或许这个体系现已变得十分难以保护和扩展,此刻能够考虑为新体系开发一个Facade类,来供给留传体系的比较明晰简略的接口,让新体系与Facade类交互,进步复用性。
- 不能过多的或许不合理的运用外观办法,运用外观办法好,仍是直接调用模块好要让体系有层次,利于保护为目的
- 外观办法在MyBatis结构运用的源码剖析
- MyBatis中的Configuration去创立MetaObject方针
事例类图: 事例代码:
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.watchMovie();
}
}
public class Facade {
private SubSystem subSystem = new SubSystem();
public void watchMovie() {
subSystem.turnOnTV();
subSystem.setCD("a movie");
subSystem.startWatching();
}
}
public class SubSystem {
public void turnOnTV() {
System.out.println("turnOnTV()");
}
public void setCD(String cd) {
System.out.println("setCD( " + cd + " )");
}
public void startWatching(){
System.out.println("startWatching()");
}
}
10.享元办法(Flyweight)
运用同享的办法来支撑许多细粒度的方针,这些方针一部分内部状况是相同的。
- Flyweight:享元方针
- IntrinsicState:内部状况,享元方针同享内部状况
- ExtrinsicState:外部状况,每个享元方针的外部状况不同
–享元办法(Flyweight Pattern)也叫 蝇量办法:运用同享技能有效地支撑许多细粒度的方针 –常用于体系底层开发,处理体系的功能问题。想数据库衔接池,里边都是创立好的衔接方针,在这些衔接方针中有咱们需求的则直接拿来用,防止从头创立,假如没有咱们需求的,则创立一个。 –享元办法能够处理重复方针的内存糟蹋的问题,当体系中有许多相似方针,需求缓冲池时。不需总是创立新方针,能够冲缓冲池里拿。这样能够下降体系内存,一同进步功率。 –享元办法金典的运用场景便是池技能了,String常量池、数据库衔接池、缓冲池等等便是享元办法的运用,享元办法是池技能的重要完结办法。
留意事项和细节
- 在享元办法这样了解,“享”就表明同享,“元”表明方针
- 体系中有许多方针,这些方针耗费许多内存,而且方针的状况大部分能够外部化时,咱们就能够考虑选用享元办法
- 用仅有标识码判别,假如在内存中有,则返回这个仅有标识码所表明的方针,用HashMap/HashTable存储
- 享元办法大大削减了方针的创立,下降了程序内存的占用,进步功率
- 享元办法进步了体系的杂乱度。需求别离出内部状况和外部状况,而外部状况具有固话特性,不应该随着内部状况的改动而改动,这是咱们运用享元办法需求留意的当地。
- 运用享元办法时,留意区分内部状况和外部状况,而且需求有一个工厂类加以操控。
- 享元办法金典的运用场景是需求缓冲池的场景,比方String常量池、数据库衔接池。
事例类图:
事例代码:
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight1 = factory.getFlyweight("aa");
Flyweight flyweight2 = factory.getFlyweight("aa");
flyweight1.doOperation("x");
flyweight2.doOperation("y");
}
}
public interface Flyweight {
void doOperation(String extrinsicState);
}
public class FlyweightFactory {
private HashMap<String, Flyweight> flyweights = new HashMap<>();
Flyweight getFlyweight(String intrinsicState) {
if (!flyweights.containsKey(intrinsicState)) {
Flyweight flyweight = new ConcreteFlyweight(intrinsicState);
flyweights.put(intrinsicState, flyweight);
}
return flyweights.get(intrinsicState);
}
}
public class ConcreteFlyweight implements Flyweight {
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void doOperation(String extrinsicState) {
System.out.println("Object address: " + System.identityHashCode(this));
System.out.println("IntrinsicState: " + intrinsicState);
System.out.println("ExtrinsicState: " + extrinsicState);
}
}
11.署理办法(Proxy)
操控对其它方针的拜访。 署理办法有不同的办法,首要有三种 静态署理、动态署理(JDK署理、接口署理)和 cglib署理(能够在内存动态的创立方针,而不需求完结接口,他是归于动态署理的范畴)。
-
静态署理办法的根本介绍
- 静态署理在运用时,需求界说接口或许父类,被署理方针(即方针方针)与署理方针一同完结相同的接口或许是承继相同父类
代码完结:
public class Tank implements Movable {
@Override
public void move() {
System.out.println("Cla...Cla...Cla");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new LogProxy(new Tank()).move();
}
}
class TimeProxy implements Movable {
Tank tank;
public TimeProxy(Tank tank) {
this.tank = tank;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
tank.move();
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
}
}
class LogProxy implements Movable {
Tank tank;
public LogProxy(Tank tank) {
this.tank = tank;
}
@Override
public void move() {
System.out.println("Tank startEngine....");
tank.move();
System.out.println("Tank shutDownEngine....");
}
}
interface Movable {
void move();
}
-
动态署理办法的根本介绍
-
署理方针不需求完结接口,可是方针方针要完结接口,不然不能用动态署理
-
署理方针的生成,是运用JDK的API,动态的在内存中构建署理方针
-
动态署理也叫做:JDK署理、接口署理
-
JDK中生成署理方针的API
- 署理类所在包:javalang.reflect.Proxy
- JDK完结署理只需求运用newProxyInstance办法,可是该办法需求接收三个参数
-
代码完结:
public class Tank implements Movable {
@Override
public void move() {
System.out.println("Cla...Cla...Cla");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
final Tank tank = new Tank();
//reflection 经过二进制字节码剖析类的特点和办法
Movable m = (Movable) Proxy.newProxyInstance(/*Tank.class.getClassLoader()*/ClassLoader.getSystemClassLoader(),
new Class[]{Movable.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method "+method.getName()+" start...");
Object o = method.invoke(tank,args);
System.out.println("method "+method.getName()+" end!");
return o;
}
}
);
Movable m1 = (Movable) Proxy.newProxyInstance(Tank.class.getClassLoader(),
new Class[]{Movable.class},
new LogInvocationHandler(tank)
);
m.move();
m1.move();
}
}
class LogInvocationHandler implements InvocationHandler{
Tank tank;
public LogInvocationHandler(Tank tank) {
this.tank = tank;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method "+method.getName()+" start...");
Object o = method.invoke(tank,args);
System.out.println("method "+method.getName()+" end!");
return o;
}
}
interface Movable{
void move();
}
-
cglib署理办法的根本介绍
-
静态署理和JDK署理办法都要求方针方针是一个完结一个接口,可是有时分方针方针仅仅一个独自的方针,并没有完结任何接口,这个时分可运用方针方针子类来完结署理,这便是cglib署理
-
cglib署理也叫子类署理,它是在内存中构建一个子类方针然后完结对方针方针功用扩展,有些书也将cglib署理归属到动态署理。
-
cglib是一个强大的高功能的代码生成包,它能够在运转期扩展java类与完结java接口,它广泛的被许多AOP的结构运用,例如Spring AOP,完结办法拦截
-
在AOP编程中怎么挑选署理办法:
- 方针方针需求完结接口,用JDK署理
- 方针方针不需求完结接口,用cglib署理
- cglib包的底层是经过运用字节码处理结构ASM来转化字节码并生成新的类
-
在进行相关代码的编写时,需求咱们提前导入cglib相关的jar包。 代码完结:
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Tank.class);
enhancer.setCallback(new TimeMethodInterceptor());
Tank tank = (Tank) enhancer.create();
tank.move();
}
}
class TimeMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(" start...");
Object result = methodProxy.invokeSuper(o,objects);
System.out.println(" end!");
return result;
}
}
class Tank{
public void move(){
System.out.println("Cla...Cla...Cla");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
12.模板办法办法(Template Mode)
界说算法结构,并将一些进程的完结推迟到子类。 经过模板办法,子类能够从头界说算法的某些进程,而不必改动算法的结构。
-
模板办法办法(Template Method Pattern),又名模板办法(Template Pattern),在一个笼统类揭露界说了履行它的办法的模板。它的子类能够按需求重写办法完结,但调用将以笼统类中界说的办法进行。
-
简略说,模板办法办法界说一个操作中的算法的骨架,而将一些进程推迟到子类中,使得子类能够不改动一个算法的结构,就能够重界说该算法的某些特定进程。
-
这品种型的规划办法归于行为型办法
-
模板办法办法的钩子办法
- 在模板办法办法的父类中, 咱们能够界说一个办法,它默许不做任何事,子类能够视状况要不要掩盖它,该办法称为“钩子”。
模板办法办法的留意事项和细节
- 根本思维是:算法只存在于一个当地,也便是父类中, 简略修正。需求修正算法时,只需修正父类的模板办法或许现已完结的某些进程,子类就会承继这些修正
- 完结了最大化代码复用。父类的模板办法和已完结的某些进程会被子类承继而直接运用。
- 即一致了算法,也供给了很大的灵敏性。父类的模板办法保证了算法的结构坚持不变,一同由子类供给部分进程的完结
- 该办法的缺乏之:每一个不同的完结都需求一个子类完结,导致类的个数添加,使得体系愈加庞大。
- 一般模板办法都加上final关键字,防止子类重写模板办法
- 模板办法办法运用场景:当要完结在某个进程,该进程要履行一系列进程,这一系列的进程根本相同,但其个别进程在完结时或许不同,通常考虑用模板办法办法来处理。
事例类图: 事例代码:
public class Client {
public static void main(String[] args) {
SoybeanMilk blockBeanMilk = new BlockBeanMilk();
SoybeanMilk redBeanMilk = new RedBeanMilk();
SoybeanMilk pureBeanMilk = new PureBeanMilk();
blockBeanMilk.make();
redBeanMilk.make();
pureBeanMilk.make();
}
}
public abstract class SoybeanMilk {
public void make() {
materialsSelect();
if (isAddCondiments()){
ingredientsAdd();
}
soak();
beat();
}
void materialsSelect() {
System.out.println("选优异的豆子");
}
abstract void ingredientsAdd();
void soak() {
System.out.println("将一切资料进行浸泡");
}
void beat() {
System.out.println("将一切资料放入豆浆机中打碎");
}
boolean isAddCondiments(){
return true;
}
}
public class RedBeanMilk extends SoybeanMilk {
@Override
public void ingredientsAdd() {
System.out.println("添加红豆");
}
}
public class PureBeanMilk extends SoybeanMilk {
@Override
void ingredientsAdd() {
}
@Override
boolean isAddCondiments() {
return false;
}
}
public class BlockBeanMilk extends SoybeanMilk {
@Override
public void ingredientsAdd() {
System.out.println("添加黑豆");
}
}
13.指令办法(Command)
指令办法(Command Pattern):在软件规划中,咱们常常需求想某些方针发送恳求,可是并不知道指令接受者是谁,也不知道被恳求的操作是哪个,咱们只需求在程序运转时指定详细的恳求接受者即可,此刻,能够运用指令办法来进行规划 指令办法使得恳求发送者与恳求接受者消除彼此之间的耦合,让方针之间的调用联系愈加灵敏,完结解耦。 在命名办法中,会将一个恳求封装为一个方针,以便运用不同参数来表明不同的恳求(即命名),一同指令办法也支撑可吊销的操作。
- Command:指令
- Receiver:指令接收者,也便是指令真正的履行者
- Invoker:经过它来调用指令
- Client:能够设置指令与指令的接收者
将指令封装成方针中,具有以下效果:
- 运用指令来参数化其它方针
- 将指令放入队列中进行排队
- 将指令的操作记录到日志中
- 支撑可吊销的操作
指令办法的留意事项和细节
- 将主张恳求的方针与履行恳求的方针解耦。主张恳求的方针是调用者,调用者只需调用指令方针的execute()办法就能够让接受者作业,而不必知道详细的接受者方针是谁、是怎么完结的,指令方针会担任让接受者履行恳求的动作,也便是说:“恳求主张者”和“恳求履行者”之间的解耦是经过指令方针完结的,指令方针起到了枢纽桥梁的效果。
- 简略规划一个指令队列。只需把指令方针放到列队。只需把指令方针放到队列,就能够多线程的履行指令
- 简略完结对恳求的吊销和重做
- 指令办法的缺乏:或许导致某些体系有过多的详细指令类,添加了体系的杂乱度,这点在运用的时分要留意
- 空指令也是一种规划办法,它为咱们省去了判空的操作。在上面的实例中,假如没有用空指令,咱们每按下一个按键都要判空,这给咱们编码带来必定的麻烦。
- 指令办法经典的运用场景:界面的一个按钮都是一条指令、模仿CMD(DOS指令)、订单的吊销/恢复、触发-反馈机制
事例类图: 事例代码:
public class Client {
public static void main(String[] args) {
RemoteController remoteController = new RemoteController();
LightReceiver lightReceiver = new LightReceiver();
Command lightOffCommand = new LightOffCommand(lightReceiver);
Command lightOnCommand = new LightOnCommand(lightReceiver);
remoteController.setCommands(0, lightOnCommand, lightOffCommand);
remoteController.onButtonWasPushed(0);
remoteController.offButtonWasPushed(0);
remoteController.undo();
}
}
public interface Command {
//履行操作
void execute();
//吊销操作
void undo();
}
public class LightOffCommand implements Command{
private LightReceiver lightReceiver;
public LightOffCommand(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
@Override
public void execute() {
lightReceiver.off();
}
@Override
public void undo() {
lightReceiver.on();
}
}
public class LightOnCommand implements Command {
private LightReceiver lightReceiver;
public LightOnCommand(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
@Override
public void execute() {
lightReceiver.on();
}
@Override
public void undo() {
lightReceiver.off();
}
}
public class Nocommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
public class LightReceiver {
public void on(){
System.out.println("灯被打开了");
}
public void off(){
System.out.println("灯被关闭了");
}
}
public class RemoteController {
private Command[] onCommands;
private Command[] offCommands;
private Command noCommand;
public RemoteController() {
this.onCommands = new Command[5];
this.offCommands = new Command[5];
for (int i = 0; i < onCommands.length; i++) {
onCommands[i] = new Nocommand();
offCommands[i] = new Nocommand();
}
}
public void setCommands(int no, Command onCommand, Command offCommand) {
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
public void onButtonWasPushed(int no) {
onCommands[no].execute();
noCommand = onCommands[no];
}
public void offButtonWasPushed(int no) {
offCommands[no].execute();
noCommand = offCommands[no];
}
public void undo() {
noCommand.undo();
}
}
14.拜访者办法(Visitor)
封装一些效果于某种数据结构的各元素的操作,它能够在不改动数据结构的前提下界说效果于这些元素的新的操作,简而言之便是为一个方针结构(比方组合结构),添加新能力。
- Visitor:拜访者,为每一个 ConcreteElement 声明一个 visit 操作
- ConcreteVisitor:详细拜访者,存储遍历进程中的累计结果
- ObjectStructure:方针结构,能够是组合结构,或许是一个调集。
拜访者办法的根本作业原理是:在被拜访的类里边加一个对外供给接待拜访者的接口
拜访者办法首要运用场景是:需求对一个方针结构中的方针进行许多不同操作(这些操作彼此没有相关),一同需求防止让这些操作“污染”这些方针的类,能够选用拜访者办法处理。
拜访者办法的留意事项和细节
长处 1.拜访者办法契合单依责任准则、让程序具有优异的扩展性、灵敏性十分高 2.拜访者办法能够对功用进行一致,能够做报表、UI、拦截器与过滤器,适用于数据结构相对安稳的体系
缺陷 1.详细元素对拜访者发布细节,也便是说拜访者关注了其他类的内部细节,这是迪米特规律所不主张的,这样形成了详细元素变更比较困难 2.违背了依靠倒转准则。拜访者依靠的是详细元素,而不是笼统元素 3.因而,假如一个体系有比较安稳的数据结构,又有常常改动的功用需求,那么拜访者办法便是比较合适的。
事例类图:
事例代码:
public class Client {
public static void main(String[] args) {
Customer customer1 = new Customer("customer1");
customer1.addOrder(new Order("order1", "item1"));
customer1.addOrder(new Order("order2", "item1"));
customer1.addOrder(new Order("order3", "item1"));
Order order = new Order("order_a");
order.addItem(new Item("item_a1"));
order.addItem(new Item("item_a2"));
order.addItem(new Item("item_a3"));
Customer customer2 = new Customer("customer2");
customer2.addOrder(order);
CustomerGroup customers = new CustomerGroup();
customers.addCustomer(customer1);
customers.addCustomer(customer2);
GeneralReport visitor = new GeneralReport();
customers.accept(visitor);
visitor.displayResults();
}
}
public interface Element {
void accept(Visitor visitor);
}
public interface Visitor {
void visit(Customer customer);
void visit(Order order);
void visit(Item item);
}
public class Item implements Element{
private String name;
Item(String name) {
this.name = name;
}
String getName() {
return name;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class Order implements Element{
private String name;
private List<Item> items = new ArrayList();
Order(String name) {
this.name = name;
}
Order(String name, String itemName) {
this.name = name;
this.addItem(new Item(itemName));
}
String getName() {
return name;
}
void addItem(Item item) {
items.add(item);
}
public void accept(Visitor visitor) {
visitor.visit(this);
for (Item item : items) {
item.accept(visitor);
}
}
}
public class Customer implements Element{
private String name;
private List<Order> orders = new ArrayList<>();
Customer(String name) {
this.name = name;
}
String getName() {
return name;
}
void addOrder(Order order) {
orders.add(order);
}
public void accept(Visitor visitor) {
visitor.visit(this);
for (Order order : orders) {
order.accept(visitor);
}
}
}
public class GeneralReport implements Visitor{
private int customersNo;
private int ordersNo;
private int itemsNo;
public void visit(Customer customer) {
System.out.println(customer.getName());
customersNo++;
}
public void visit(Order order) {
System.out.println(order.getName());
ordersNo++;
}
public void visit(Item item) {
System.out.println(item.getName());
itemsNo++;
}
public void displayResults() {
System.out.println("Number of customers: " + customersNo);
System.out.println("Number of orders: " + ordersNo);
System.out.println("Number of items: " + itemsNo);
}
}
public class CustomerGroup {
private List<Customer> customers = new ArrayList<>();
void accept(Visitor visitor) {
for (Customer customer : customers) {
customer.accept(visitor);
}
}
void addCustomer(Customer customer) {
customers.add(customer);
}
}
15.迭代器(Iterator)
供给一种顺序拜访聚合方针元素的办法,而且不暴露聚合方针的内部表明。
- Aggregate 是聚合类,其间 createIterator() 办法能够产生一个 Iterator;
- Iterator 首要界说了 hasNext() 和 next() 办法;
- Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需求组合 Iterator。
迭代器办法的留意事项和细节:
- 长处:
- 供给一个一致的办法遍历方针,客户不必再考虑聚合的类型,运用一种办法就能够遍历方针了
- 躲藏了聚合的内部结构,客户端要遍历聚合的时分只能 取到迭代器,而不会知道聚合的组成。
- 供给了一种规划思维,便是一个类应该只需一个引起改动的原因(叫做单一责任准则)。在聚合类中,咱们把迭代器分隔,便是要把办理方针调集和遍历方针调集的责任分隔,这样一来调集改动的话,只影响到聚合方针。而假如遍历办法改动的话,只影响到了迭代器。
- 当要展示一组相似方针,或许遍历一组相同方针时运用,合适运用迭代器办法。
- 缺陷
- 每个聚合方针都要一个迭代器,会生成多个迭代器不好办理类。
事例类图:
事例代码:
public interface Aggregate {
Iterator createIterator();
}
public class ConcreteAggregate implements Aggregate {
private Integer[] items;
public ConcreteAggregate() {
items = new Integer[10];
for (int i = 0; i < items.length; i++) {
items[i] = i;
}
}
@Override
public Iterator createIterator() {
return new ConcreteIterator<Integer>(items);
}
}
public class ConcreteIterator<Item> implements Iterator {
private Item[] items;
private int position = 0;
public ConcreteIterator(Item[] items) {
this.items = items;
}
@Override
public Object next() {
return items[position++];
}
@Override
public boolean hasNext() {
return position < items.length;
}
}
public class Client {
public static void main(String[] args) {
Aggregate aggregate = new ConcreteAggregate();
Iterator<Integer> iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
其实关于迭代器办法,jdk中的容器类傍边遍历的完结便是一个比较经典的比如。我们能够去看看源码吸收一下。
16.调查者办法(Observer)
界说方针之间的一对多依靠,当一个方针状况改动时,它的一切依靠都会收到告诉而且自动更新状况。
主题(Subject)是被调查的方针,而其一切依靠者(Observer)称为调查者。 调查者办法的优点:
- 调查者办法规划后,会以调集的办法来办理用户(Observer),包括注册,移除和告诉。
- 这样,咱们添加调查者(这儿能够了解成一个新的公告板),就不需求去修正中心类WeatherData不会修正代码遵守了ocp准则
调查者办法在jdk运用的源码剖析:
- 办法人物剖析
- Observable的效果和地位等价于咱们前面讲过Subject
- Observable是类,不是接口,类中现已完结了中心的办法,即办理Observer的办法add..delete…notify
- Observer的效果和地位等价于咱们前面讲过的Onserver,有update
- Observable和Observer的运用办法和前面讲过的相同,仅仅Observable是类,经过承继来完结调查者办法。
事例类图: 事例代码:
public class Client {
public static void main(String[] args) {
//创立一个WeatherData
WeatherData weatherData = new WeatherData();
CurrentConditions currentConditions = new CurrentConditions();
weatherData.registerObserver(currentConditions);
System.out.println("告诉各个注册的调查者,看看信息");
weatherData.setData(10f,20f,30f);
weatherData.removeObserver(currentConditions);
}
}
//调查者接口,有调查者来完结
public interface Observer {
void update(float temperature, float pressure, float humidity);
}
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
public class CurrentConditions implements Observer{
private float temperature;
private float pressure;
private float humidity;
@Override
public void update(float temperature, float pressure, float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
private void display() {
System.out.println("*****Today mTemperature:"+temperature+"*****");
System.out.println("*****Today mPressure:"+pressure+"*****");
System.out.println("*****Today mHumidity:"+humidity+"*****");
}
}
public class WeatherData implements Subject {
private float temperature;
private float pressure;
private float humidity;
private List<Observer> observerList = new ArrayList<>();
public WeatherData() {
}
public float getTemperature() {
return temperature;
}
public float getPressure() {
return pressure;
}
public float getHumidity() {
return humidity;
}
public void dataChange() {
notifyObservers();
}
public void setData(float temperature,float pressure,float humidity){
this.temperature =temperature;
this.pressure =pressure;
this.humidity = humidity;
dataChange();
}
@Override
public void registerObserver(Observer o) {
observerList.add(o);
}
@Override
public void removeObserver(Observer o) {
if (observerList.contains(o)){
observerList.remove(o);
}
}
@Override
public void notifyObservers() {
Iterator<Observer> iterator = observerList.iterator();
while (iterator.hasNext()) {
iterator.next().update(getTemperature(), getPressure(), getHumidity());
}
}
}
17.中介者办法(Mediator)
集中相关方针之间杂乱的沟通和操控办法。
- Mediator:中介者,界说一个接口用于与各搭档(Colleague)方针通信。
- Colleague:搭档,相关方针
中介者办法的留意事项和细节:
- 多个类相互耦合,会形成网状结构,运用中介者办法将网状结构别离为星型结构,进行解耦。
- 削减类间依靠,下降了耦合,契合迪米特准则
- 中介者承当了较多的责任,一旦中介者呈现了问题,整个体系就会受到影响
- 假如规划不妥,中介者方针自身变得过于杂乱,这点在实践运用时,要特别留意
事例类图: 事例代码:
public class Client {
public static void main(String[] args) {
Alarm alarm = new Alarm();
CoffeePot coffeePot = new CoffeePot();
Calender calender = new Calender();
Sprinkler sprinkler = new Sprinkler();
Mediator mediator = new ConcreteMediator(alarm, coffeePot, calender, sprinkler);
// 闹钟事情到达,调用中介者就能够操作相关方针
alarm.onEvent(mediator);
}
}
public abstract class Colleague {
public abstract void onEvent(Mediator mediator);
}
public abstract class Mediator {
public abstract void doEvent(String eventType);
}
public class ConcreteMediator extends Mediator {
private Alarm alarm;
private CoffeePot coffeePot;
private Calender calender;
private Sprinkler sprinkler;
public ConcreteMediator(Alarm alarm, CoffeePot coffeePot, Calender calender, Sprinkler sprinkler) {
this.alarm = alarm;
this.coffeePot = coffeePot;
this.calender = calender;
this.sprinkler = sprinkler;
}
@Override
public void doEvent(String eventType) {
switch (eventType) {
case "alarm":
doAlarmEvent();
break;
case "coffeePot":
doCoffeePotEvent();
break;
case "calender":
doCalenderEvent();
break;
default:
doSprinklerEvent();
}
}
public void doAlarmEvent() {
alarm.doAlarm();
}
public void doCoffeePotEvent() {
coffeePot.doCoffeePot();
// ...
}
public void doCalenderEvent() {
// ...
calender.doCalender();
}
public void doSprinklerEvent() {
// ...
sprinkler.doSprinkler();
}
}
public class Sprinkler extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("sprinkler");
}
public void doSprinkler() {
System.out.println("doSprinkler()");
}
}
public class CoffeePot extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("coffeePot");
}
public void doCoffeePot() {
System.out.println("doCoffeePot()");
}
}
public class Calender extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("calender");
}
public void doCalender() {
System.out.println("doCalender()");
}
}
public class Alarm extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("alarm");
}
public void doAlarm() {
System.out.println("doAlarm()");
}
}
18.备忘录办法(Memento)
在不违背封装的状况下取得方针的内部状况,然后在需求时能够将方针恢复到最初状况。
- Originator:原始方针
- Caretaker:担任保存好备忘录
- Memento:备忘录,存储原始方针的状况。备忘录实践上有两个接口,一个是供给给 Caretaker 的窄接口:它只能将备忘录传递给其它方针;一个是供给给 Originator 的宽接口,答应它拜访到从前状况所需的一切数据。抱负状况是只答应 Originator 拜访本备忘录的内部状况。
备忘录办法的留意事项和细节
- 给用户供给了一种能够恢复状况的机制,能够运用户能够比较便利地回到某个前史的状况。
- 完结了信息的封装,使得用户不需求关怀状况的保存细节
- 假如类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会耗费必定的内存,这个需求留意
- 适用的运用场景:1、后悔药 2、打游戏时的存档 3、Windwos里的ctrl+z 4、IE中的撤退 4、数据库的事务办理
- 为了节约内存,备忘录办法能够和原型办法配合运用
事例类图: 事例代码:
public class Client {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
GameRole gameRole = new GameRole(100, 200);
System.out.println("============大战前=============");
gameRole.showRoleStatus();
caretaker.saveMementoToList(gameRole.createMemento());
gameRole.setVit(10);
gameRole.setDef(399);
caretaker.saveMementoToList(gameRole.createMemento());
System.out.println("============大战后=============");
gameRole.showRoleStatus();
gameRole.recoverStatusFromMemento(caretaker.getMemento(0));
System.out.println("===========恢复后=============");
gameRole.showRoleStatus();
}
}
public class Memento {
private int vit;
private int def;
public Memento(int vit, int def) {
this.vit = vit;
this.def = def;
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
public class GameRole {
private int vit;
private int def;
public GameRole(int vit, int def) {
this.vit = vit;
this.def = def;
}
public Memento createMemento() {
return new Memento(vit, def);
}
public void recoverStatusFromMemento(Memento memento) {
this.vit = memento.getVit();
this.def = memento.getDef();
}
public void showRoleStatus() {
System.out.println("游戏人物当前的攻击力:" + this.vit + "\t防御力:" + this.def);
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
public class Caretaker {
private ArrayList<Memento> mementoArrayList = new ArrayList<>();
public Memento getMemento(int numStatus) {
return mementoArrayList.get(numStatus);
}
public void saveMementoToList(Memento memento) {
mementoArrayList.add(memento);
}
}
19.战略办法(Strategy)
界说一系列算法,封装每个算法,并使它们能够互换。战略办法能够让算法独立于运用它的客户端。
- Strategy 接口界说了一个算法族,它们都完结了 behavior() 办法。
- Context 是运用到该算法族的类,其间的 doSomething() 办法会调用 behavior(),setStrategy(Strategy) 办法能够动态地改动 strategy 方针,也便是说能动态地改动 Context 所运用的算法。
与状况办法的比较
状况办法的类图和战略办法相似,而且都是能够动态改动方针的行为。可是状况办法是经过状况搬运来改动 Context 所组合的 State 方针,而战略办法是经过 Context 自身的决策来改动组合的 Strategy 方针。所谓的状况搬运,是指 Context 在运转进程中由于一些条件产生改动而使得 State 方针产生改动,留意必需求是在运转进程中。
状况办法首要是用来处理状况搬运的问题,当状况产生搬运了,那么 Context 方针就会改动它的行为;而战略办法首要是用来封装一组能够相互代替的算法族,而且能够根据需求动态地去替换 Context 运用的算法。
战略办法的留意事项和细节
- 战略办法的关键是:剖析项目中改动部分与不变部分
- 战略办法的中心思维是:多用组合/聚合 少用承继;用行为类组合,而不是行为的承继。更有弹性
- 表现了“对修正关闭,对扩展敞开”准则,客户端添加行为不必修正原有代码,只需求添加一种战略(或许行为)即可,防止了运用多重搬运句子(if…else if…else)
- 供给了能够替换承继联系的办法:战略办法将算法封装在独立的Strategy类中的使得你能够独立于其Context改动它,使它易于切换、易于扩展
- 需求留意的是:每添加一个战略就要添加一个类,当战略过多是会导致类数目庞大
事例类图:
事例代码:
public class Client {
public static void main(String[] args) {
WildDunk wildDunk = new WildDunk(new GoodFlyStrategy(),new GoodQuackStrategy());
HomeDunk homeDunk = new HomeDunk(new BadFlyStrategy(),new BadQuackStrategy());
wildDunk.quack();
wildDunk.fly();
homeDunk.quack();
homeDunk.fly();
}
}
public interface FlyStrategy {
void fly();
}
public interface QuackStrategy {
void quack();
}
public abstract class Dunk {
private FlyStrategy flyStrategy;
private QuackStrategy quackStrategy;
public Dunk(FlyStrategy flyStrategy, QuackStrategy quackStrategy) {
this.flyStrategy = flyStrategy;
this.quackStrategy = quackStrategy;
}
public void quack() throws NullPointerException {
if (quackStrategy==null){
throw new NullPointerException();
}
quackStrategy.quack();
}
public void fly(){
if (flyStrategy==null){
throw new NullPointerException();
}
flyStrategy.fly();
}
}
public class WildDunk extends Dunk {
public WildDunk(FlyStrategy flyStrategy, QuackStrategy quackStrategy) {
super(flyStrategy, quackStrategy);
}
}
public class HomeDunk extends Dunk{
public HomeDunk(FlyStrategy flyStrategy, QuackStrategy quackStrategy) {
super(flyStrategy,quackStrategy);
}
}
public class BadFlyStrategy implements FlyStrategy {
@Override
public void fly() {
System.out.println("翱翔技能很差的鸭子!");
}
}
public class BadQuackStrategy implements QuackStrategy {
@Override
public void quack() {
System.out.println("叫声难听的鸭子!");
}
}
public class GoodQuackStrategy implements QuackStrategy {
@Override
public void quack() {
System.out.println("叫声好听的鸭子!");
}
}
public class NoFlyStrategy implements FlyStrategy {
@Override
public void fly() {
System.out.println("不会翱翔技能的鸭子!");
}
}
public class NoQuackStrategy implements QuackStrategy {
@Override
public void quack() {
System.out.println("不会叫的鸭子");
}
}
战略办法在日常开发中有许多场景都能运用,这个办法需求咱们要点把握!
20.责任链办法(Chain of Responsibility)
使多个方针都有时机处理恳求,然后防止恳求的发送者和接收者之间的耦合联系。将这些方针连成一条链,并沿着这条链发送该恳求,直到有一个方针处理它为止。 责任链办法通常每个接受者都包含对另一个接受者的引证。假如一个方针不能处理改恳求,那么它会把相同的恳求传给下一个接受者,以此类推。
责任链办法在SpringMVC结构运用:
- springMVC恳求的流程图中,履行了拦截器相关办法interceptor.preHandler等等
- 在处理SpringMVC恳求时,运用到责任链办法仍是用到适配器办法
- HandlerExecutionChain首要担任的是恳求拦截器的履行和恳求处理,可是他自身不处理恳求,仅仅将恳求分配给链上注册处理器履行,这是责任链完结办法,削减责任链自身与处理逻辑之间的耦合,标准了处理流程
- HandlerExecutionChain保护了HandlerInterceptor的调集,能够向其间注册相应的拦截器。
其实责任链还在咱们比较了解的servlet中的过滤器中有运用。 责任链办法的留意事项和细节:
- 将恳求和处理分隔,完结解耦,进步体系的灵敏性
- 简化了方针,使方针不需求知道链的结构
- 功能会受到影响,特别是在链比较长的时分,因而需操控链中最节操点数量,一般经过在Handler中设置一个最节操点数量,在setNext()办法中判别是否现已超过阈值,超过则不答应该链建立,防止呈现超长链无意识地破坏体系功能
- 调试不便利。采用了相似递归的办法,调试时逻辑或许比较杂乱
- 最佳引证场景:有多个方针能够处理同一个恳求时,比方:多级恳求、请假/加薪等批阅流程、java web中tomcat对encoding的处理、拦截器。
事例类图: 事例代码:
public class Client {
public static void main(String[] args) {
PurchaseRequest purchaseRequest = new PurchaseRequest(16887,"图书",10000);
DepartmentApprover departmentApprover = new DepartmentApprover("李系长");
departmentApprover.processRequest(purchaseRequest);
}
}
public abstract class Approver {
protected Approver approver;
protected String name;
public Approver(String name) {
this.name = name;
}
public void setApprover(Approver approver) {
this.approver = approver;
}
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
public class CollegeApprover extends Approver {
public CollegeApprover(String name) {
super(name);
approver = new ViceSchoolMasterApprover("李副校长");
setApprover(approver);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice()<10000){
System.out.println(this.name+"正在处理价格为"+purchaseRequest.getPrice()+"的项目");
}else{
approver.processRequest(purchaseRequest);
}
}
}
public class DepartmentApprover extends Approver{
public DepartmentApprover( String name) {
super(name);
approver = new CollegeApprover("王院长");
setApprover(approver);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (1000<purchaseRequest.getPrice()&&purchaseRequest.getPrice()<3000){
System.out.println(this.name+"正在处理价格为"+purchaseRequest.getPrice()+"的项目");
}else {
approver.processRequest(purchaseRequest);
}
}
}
public class SchoolMasterApprover extends Approver {
public SchoolMasterApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice()<50000){
System.out.println(this.name+"正在处理价格为"+purchaseRequest.getPrice()+"的项目");
}else{
System.out.println("现已无法进行处理");
}
}
}
public class ViceSchoolMasterApprover extends Approver{
public ViceSchoolMasterApprover(String name) {
super(name);
approver = new SchoolMasterApprover("张校长");
setApprover(approver);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice()<30000){
System.out.println(this.name+"正在处理价格为"+purchaseRequest.getPrice()+"的项目");
}else{
approver.processRequest(purchaseRequest);
}
}
}
public class PurchaseRequest {
private int price;
private String type;
private int weight;
public PurchaseRequest(int price, String type, int weight) {
this.price = price;
this.type = type;
this.weight = weight;
}
@Override
public String toString() {
return "PurchaseRequest{" +
"price=" + price +
", type='" + type + '\'' +
", weight=" + weight +
'}';
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}