概述
设计模式是针对某一类问题的最优解决方案,是从许多优秀的软件系统中总结出的。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{
@Override
public void run() {
System.out.println("我是一台PRO5");
}
}
class PRO6 implements MeizuPhone{
@Override
public 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{
@Override
public void run() {
System.out.println("我是一台PRO5");
}
}
class MX5 implements MeizuPhone{
@Override
public void run() {
System.out.println("我是一台MX5");
}
}
interface IFactory{//抽象的工厂
MeizuPhone produce();
}
//工厂*2
class BigFactory implements IFactory{
@Override
public MeizuPhone produce() {
return new PRO5();
}
}
class SmallFactory implements IFactory{
@Override
public 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{
@Override
public void run() {
System.out.println("我是一台PRO5");
}
}
class MX5 implements MeizuPhone{
@Override
public void run() {
System.out.println("我是一台MX5");
}
}
class EP21 implements Headset{
@Override
public void play() {
System.out.println("我是一副EP21");
}
}
class EP30 implements Headset{
@Override
public void play() {
System.out.println("我是一台EP30");
}
}
//抽象工厂
interface IFactory{
MeizuPhone producePhone();
Headset produceHeadset();
}
//具体工厂*2
class BigFactory implements IFactory{
@Override
public MeizuPhone producePhone() {
return new PRO5();
}
@Override
public Headset produceHeadset() {
return new EP30();
}
}
//具体工厂*2
class SmallFactory implements IFactory{
@Override
public MeizuPhone producePhone() {
return new MX5();
}
@Override
public 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 {
@Override
public void buildString() {
cup.setString("生产绳子...");
System.out.println("生产绳子...");
}
@Override
public void buildCap() {
cup.setCap("生产帽子...");
System.out.println("生产帽子...");
}
@Override
public 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 {
@Override
public String pack() {
return "Wrapper";
}
}
public class Bottle implements Packing {
@Override
public String pack() {
return "Bottle";
}
}
//每个套餐都有一个汉堡和一个饮料,汉堡用包装饮料用杯装
public abstract class Burger implements Item {
@Override
public Packing packing() {
return new Wrapper();
}
@Override
public abstract float price();
}
public abstract class ColdDrink implements Item {
@Override
public Packing packing() {
return new Bottle();
}
@Override
public abstract float price();
}
//汉堡和饮料的种类有很多
public class ChickenBurger extends Burger {
@Override
public float price() {
return 50.5f;
}
@Override
public String name() {
return "Chicken Burger";
}
}
public class VegBurger extends Burger {
@Override
public float price() {
return 25.0f;
}
@Override
public String name() {
return "Veg Burger";
}
}
public class Coke extends ColdDrink {
@Override
public float price() {
return 30.0f;
}
@Override
public String name() {
return "Coke";
}
}
public class Pepsi extends ColdDrink {
@Override
public float price() {
return 35.0f;
}
@Override
public 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.0
Non-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;
}
@Override
public 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值是不同的。
但是我们还可以看到,对象私有的成员变量也有成功复制下来,说明我们的复制是成功的。