概述
设计模式是针对某一类问题的最优解决方案,是从许多优秀的软件系统中总结出的。Java中设计模式(java design patterns)通常有23种。
模式可以分成3类:创建型、行为型和结构型。
创建型模式
创建型模式涉及对象的实例化,特点是不让用户代码依赖于对象的创建或排列方式,避免用户直接使用new创建对象。创建型模式有以下5个:
工厂方法模式、抽象工厂方法模式、生成器模式、原型模式和单例模式。
行为型模式
行为型模式涉及怎样合理的设计对象之间的交互通信,以及怎样合理为对..象分配职责,让设计富有弹性,易维护,易复用。行为型模式有以下11个:
责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。
结构型模式
结构型模式涉及如何组合类和对象以形成更大的结构,和类有关的结构型模式涉及如何合理使用继承机制;和对象有关的结构型模式涉及如何合理的使用对象组合机制。结构型模式有以下7个:
适配器模式、组合模式、代理模式、享元模式、外观模式、桥接模式和装饰模式。
模式中涉及的重要角色,会在描述中(加粗字体)介绍出来。下面就逐一介绍。
上述概述截取自网络,通俗易懂。
1.单例模式
Ensure a class only has one instance,and provide a global point of access to it.
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
首先理解这段话,抓住其中的重点信息:一个类仅有一个实例并提供全局访问。
从上我们可以抓到几个关键点:
1.既然是一个类的实例,必然需要构造函数。
2.为了保证有且仅有一个实例,需要对构造函数进行私有化。
3.提供全局访问的接口,说明需要有一个function将唯一实例提供给全局。
由此,我们能知道单例模式的实现方式:
//饿汉式(线程安全,调用效率高,但是不能延时加载)public class ImageLoader{private static ImageLoader instance = new ImageLoader;private ImageLoader(){}public static ImageLoader getInstance(){return instance;}
}
所谓饿汉式,就是直接创建一个静态对象,不管来多少人调用,都只提供它,呈现饥饿提供的方式,故名饿汉式。
//懒汉式(线程安全,调用效率不高,但是能延时加载)public class SingletonDemo2 {//类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)private static SingletonDemo2 instance;//构造器私有化private SingletonDemo2(){}//方法同步,调用效率低public static synchronized SingletonDemo2 getInstance(){if(instance==null){instance=new SingletonDemo2();}return instance;}
}
所谓懒汉式,就是当需要实例对象且没有实例对象的时候才进行创建,否则不创建,以一种很懒的形式处理,故名懒汉式。
单例的使用场景:作为一个喜欢对战类游戏的我来讲,就好比在对局游戏中双方的基地水晶,无论局面如何变化,各自的水晶都只有一个,这里的水晶对象就可以理解为单例的对象。
2.工厂方法模式
Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclassess.
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
同上面的处理方式.
首先来品它的定义,抓住它的重点:定义一个接口,作用是创建对象,而且能够让子类自行选择需要实例化的类。
那么我们可以想象一下它的处理方式:
1.有一个接口。
2.由于需要创建对象,自然需要类去实现这个接口。
3.而且子类可以自己决定实例化哪一个类,因此不少于两个类实现同一个接口。
现在来想象一个场景,有一个接口A,它有一个函数funciton。并且有B和C实现这个接口。那么我们可以通过创建不同的B和C的对象,从而调用其对应的function功能。
//演示简单工厂
public class SimpleFactory {public static void main(String args[]) throws Exception{Factory factory = new Factory();factory.produce("PRO5").run();factory.produce("PRO6").run();}
}
//抽象产品
interface MeizuPhone{void run();
}
//具体产品X2
class PRO5 implements MeizuPhone{@Overridepublic void run() {System.out.println("我是一台PRO5");}
}
class PRO6 implements MeizuPhone{@Overridepublic void run() {System.out.println("我是一台PRO6");}
}
//工厂
class Factory{MeizuPhone produce(String product) throws Exception{if(product.equals("PRO5"))return new PRO5();else if(product.equals("PRO6"))return new PRO6();throw new Exception("No Such Class");}
}
上面实现的就是简单工厂模式,它可以自主选择需要创建的对象。但是它有个缺点,就是一旦我需要再添加一个类实现接口,我需要调整很多东西(不仅需要添加类,还要在Factory中添加一层条件判断语句)。
//工厂方法模式
public class FactoryMethod {public static void main(String args[]){IFactory bigfactory;bigfactory = new SmallFactory();bigfactory.produce().run();bigfactory = new BigFactory();bigfactory.produce().run();}
}
//抽象产品
interface MeizuPhone{void run();
}
//具体产品*2
class PRO5 implements MeizuPhone{@Overridepublic void run() {System.out.println("我是一台PRO5");}
}
class MX5 implements MeizuPhone{@Overridepublic void run() {System.out.println("我是一台MX5");}
}
interface IFactory{//抽象的工厂MeizuPhone produce();
}
//工厂*2
class BigFactory implements IFactory{@Overridepublic MeizuPhone produce() {return new PRO5();}
}
class SmallFactory implements IFactory{@Overridepublic MeizuPhone produce() {return new MX5();}
}
以上是工厂方法模式,它使用两个厂去生产两件商品。相比简单工厂,它的解耦性强,不会和别的内容产生牵连。
3.抽象工厂模式
Provide an interface for creating families of related or dependent objects without specifying their concrete classess.
提供一个创建一系列或相互依赖对象的接口,而无须指定他们的具体的类。
同上抓重点:提供一个接口,用于创建一系列对象。
我们大致知道:
1.我们需要一个接口。
2.我们需要多个实现类。
3.我们可以通过这些实现类创建一系列对象。
//抽象工厂模式
public class AbstractFactory {public static void main(String args[]){IFactory bigfactory = new BigFactory();IFactory smallfactory = new BigFactory();bigfactory.producePhone().run();bigfactory.produceHeadset().play();smallfactory.producePhone().run();smallfactory.produceHeadset().play();}
}
//抽象产品*2
interface Headset{void play();
}
//抽象产品
interface MeizuPhone{void run();
}
//具体产品*2*2
class PRO5 implements MeizuPhone{@Overridepublic void run() {System.out.println("我是一台PRO5");}
}
class MX5 implements MeizuPhone{@Overridepublic void run() {System.out.println("我是一台MX5");}
}
class EP21 implements Headset{@Overridepublic void play() {System.out.println("我是一副EP21");}
}
class EP30 implements Headset{@Overridepublic void play() {System.out.println("我是一台EP30");}
}
//抽象工厂
interface IFactory{MeizuPhone producePhone();Headset produceHeadset();
}
//具体工厂*2
class BigFactory implements IFactory{@Overridepublic MeizuPhone producePhone() {return new PRO5();}@Overridepublic Headset produceHeadset() {return new EP30();}
}
//具体工厂*2
class SmallFactory implements IFactory{@Overridepublic MeizuPhone producePhone() {return new MX5();}@Overridepublic Headset produceHeadset() {return new EP21();}
}
由上面我们可以看到,一个工厂不仅可以生产一中产品,当我们需要生产多个不同的商品时只需调用不同的方法即可,而且添加一种不同的商品,只需在接口中添加一个方法,并在实体类中间继承实现该方法即可。
简述三者区别:
简单工厂模式的工厂是个实体类,它是通过条件控制进行的选择。工厂方法模式是对简单工厂的延伸,它将工厂类抽象化,允许多个工厂对它进行实现,从而对简单工厂模式进行延伸。而且,当工厂方法的出现,会导致工厂对象数量的增加,于是产生了抽象工厂模式,一个抽象工厂不仅仅只能产生一个产品对象,可以减少重复对象的产生。
工厂模式主要就涉及上面介绍的三种:
- 简单工厂模式是由一个具体的类去创建其他类的实例,父类是相同的,父类是具体的。
- 工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。
- 抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。它针对的是有多个产品的等级结构。而工厂方法模式针对的是一个产品的等级结构。
4、生成器模式(Builder Pattern)
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
将一个复杂对象的构建与它的表示分离,使同样的构建过程可以创建不同的表示。
首先理解什么叫将复杂对象的构建和表示分离,以及什么叫同样的构建过程可以创建不同的表示。
举个例子,工厂想要生产一个水杯,不管水杯的品种类型或者材质,必须要经历的三个步骤是生产绳子,生产杯盖,生产杯身。但是对于消费者而言是不关注这些步骤的。
首先需要一个Cup类
public class Cup {private String string; //绳子private String cap; //帽子private String cupBody; //杯体public void setString(String string) {this.string = string;}public void setCap(String cap) {this.cap = cap;}public void setCupBody(String cupBody) {this.cupBody = cupBody;}public void show() {System.out.println("杯子生产完毕");}}
然后需要一个Builder类
public abstract class Builder {protected Cup cup = new Cup();public abstract void buildString();public abstract void buildCap();public abstract void buildCupBody();public Cup getResult() {return cup;}}
然后需要一个ClassCup用于实际生产
public class ClassCup extends Builder {@Overridepublic void buildString() {cup.setString("生产绳子...");System.out.println("生产绳子...");}@Overridepublic void buildCap() {cup.setCap("生产帽子...");System.out.println("生产帽子...");}@Overridepublic void buildCupBody() {cup.setCupBody("生产杯体...");System.out.println("生产杯体...");}}
然后需要一个Directior类
public class Director {private Builder builder;public Director(Builder builder) {this.builder = builder;}public void create() {builder.buildString();builder.buildCap();builder.buildCupBody();builder.getResult().show();}public static void main(String[] args) {Director d = new Director(new ClassCup());d.create();}}
结果
下面在举一个炸鸡汉堡店的例子供给理解:
//创建一个套餐列表
public interface Item {public String name();public Packing packing();public float price();
}//打包方式又有不同
public interface Packing {public String pack();
}//有两种打包方式
class Wrapper implements Packing {@Overridepublic String pack() {return "Wrapper";}
}public class Bottle implements Packing {@Overridepublic String pack() {return "Bottle";}
}//每个套餐都有一个汉堡和一个饮料,汉堡用包装饮料用杯装
public abstract class Burger implements Item {@Overridepublic Packing packing() {return new Wrapper();}@Overridepublic abstract float price();
}public abstract class ColdDrink implements Item {@Overridepublic Packing packing() {return new Bottle();}@Overridepublic abstract float price();
}//汉堡和饮料的种类有很多
public class ChickenBurger extends Burger {@Overridepublic float price() {return 50.5f;}@Overridepublic String name() {return "Chicken Burger";}
}public class VegBurger extends Burger {@Overridepublic float price() {return 25.0f;}@Overridepublic String name() {return "Veg Burger";}
}public class Coke extends ColdDrink {@Overridepublic float price() {return 30.0f;}@Overridepublic String name() {return "Coke";}
}public class Pepsi extends ColdDrink {@Overridepublic float price() {return 35.0f;}@Overridepublic String name() {return "Pepsi";}
}//列出菜单,包括套餐的内容和价格
import java.util.ArrayList;
import java.util.List;public class Meal {private List<Item> items = new ArrayList<Item>(); public void addItem(Item item){items.add(item);}public float getCost(){float cost = 0.0f;for (Item item : items) {cost += item.price();} return cost;}public void showItems(){for (Item item : items) {System.out.print("Item : "+item.name());System.out.print(", Packing : "+item.packing().pack());System.out.println(", Price : "+item.price());} }
}//商家自定义套餐种类可供用户选择
public class MealBuilder {public Meal prepareVegMeal (){Meal meal = new Meal();meal.addItem(new VegBurger());meal.addItem(new Coke());return meal;} public Meal prepareNonVegMeal (){Meal meal = new Meal();meal.addItem(new ChickenBurger());meal.addItem(new Pepsi());return meal;}
}//用户点单
public class BuilderPatternDemo {public static void main(String[] args) {MealBuilder mealBuilder = new MealBuilder();Meal vegMeal = mealBuilder.prepareVegMeal();System.out.println("Veg Meal");vegMeal.showItems();System.out.println("Total Cost: " +vegMeal.getCost());Meal nonVegMeal = mealBuilder.prepareNonVegMeal();System.out.println("\n\nNon-Veg Meal");nonVegMeal.showItems();System.out.println("Total Cost: " +nonVegMeal.getCost());}
}//打印凭据
Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5
总的来说:建造者模式将复杂产品的构建过程封装分解在不同的方法中,使得创建过程非常清晰,能够让我们更加精确的控制复杂产品对象的创建过程,同时它隔离了复杂产品对象的创建和使用,使得相同的创建过程能够创建不同的产品。但是如果某个产品的内部结构过于复杂,将会导致整个系统变得非常庞大,不利于控制,同时若几个产品之间存在较大的差异,则不适用建造者模式,毕竟这个世界上存在相同点大的两个产品并不是很多,所以它的使用范围有限。
5、原型模式(Prototype Pattern)
Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.
用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
通过定义,我们能发现,它通过原型指定待创建对象的种类,并且新创建的对象是通过原型复制而来。
举个例子:我们想要模仿一架直升飞机,当飞机的内部元器件对我们是不可见的时候(私有成员),我们就算模仿的再像,也只是模仿了它的外观,我们模仿出来的东西是不能起飞的。那我们想要模仿一架一模一样的飞机就可以交给飞机,将它克隆一份自己。
//具体原型
class Plane implements Protoype,Cloneable{private String name; //飞机名称private String type; //飞机型号private Plane(Plane plane){name = plane.getName();type = plane.getType();}public Plane(){name = "自由号";type = "133";}public String getName(){return name;}public String getType(){return type;}@Overridepublic Object cloneSelf() {return new Plane(this);}
}//抽象原型
interface Protoype{public Object cloneSelf();
}//使用效果
public class Test {public static void main(String[] args) {Plane plane = new Plane();System.out.println(plane.toString());System.out.println(plane.getName() + "," + plane.getType());System.out.println("---------------------------------------");Plane clonePlane = (Plane)plane.cloneSelf();System.out.println(clonePlane.toString());System.out.println(clonePlane.getName() + "," + clonePlane.getType());}
}//结果Plane@35851384
自由号,133
---------------------------------------
Plane@649d209a
自由号,133
由结果我们可以看到,我们确实复制了一架飞机,因为对象的hash值是不同的。
但是我们还可以看到,对象私有的成员变量也有成功复制下来,说明我们的复制是成功的。