23种设计模式之创建型模式(单例、工厂方法、抽象工厂、生成器和原型模式)

news/2024/11/29 12:40:28/

概述


    设计模式是针对某一类问题的最优解决方案,是从许多优秀的软件系统中总结出的。

    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值是不同的。

但是我们还可以看到,对象私有的成员变量也有成功复制下来,说明我们的复制是成功的。


http://www.ppmy.cn/news/112813.html

相关文章

PL/SQL之索引和分区

一、索引 --index 数据库中的索引和目录的概念类似&#xff0c;如果某个列出现在查询的条件中&#xff0c;而该列的数据是无序的&#xff0c;那么查询时只能一行一行去扫描。 创建索引就是对某些特定列中的数据进行排序&#xff0c;生成独立的索引表&#xff0c; 当在某个…

ASCII Unicode UTF-8等等编码介绍

目录 背景 Unicode UTF-8 ISO-8859-1 GB2312和GBK ANSI UTF-16LE 和UTF-16BE UTF-16 LE 和BE是什么 如何处理字节序问题 "带有BOM的UTF-8"又是什么&#xff1f; 背景 由于计算机是美国人发明的&#xff0c;因此最早只有127个字母被编码到计算机中&#x…

【后缀数组/SAM+边分树合并】LGP5115 Check,Check,Check one two!

【题目】 原题地址 给定一个字符串 S S S&#xff0c;求 ∑ 1 ≤ i < j ≤ n l c p ( i , j ) l c s ( i , j ) [ l c p ( i , j ) ≤ k 1 ] [ l c s ( i , j ) ≤ k 2 ] \sum_{1\leq i<j\leq n}lcp(i,j)lcs(i,j)[lcp(i,j)\leq k_1][lcs(i,j)\leq k_2] 1≤i<j≤n∑​l…

hdu5115-Dire Wolf【区间dp】

正题 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid5115 题目大意 有 n n n只狼&#xff0c;击败第 i i i只狼会扣 a i a_i ai​加上于其相邻的狼的 b l b r b_lb_r bl​br​点 h p hp hp。注意该狼被击败后会使原来于其相邻的狼变的相邻。 解题思路 显然区间 d p …

HDU 5115 Dire Wolf 区间dp

Dire Wolf Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid5115 Description Dire wolves, also known as Dark wolves, are extraordinarily large and powerful wolves. Many, if not all, Dire Wolves appear to originate …

HDU - 5115 经典区间dp

题意&#xff1a;给定n个狼的攻击值ai和附加攻击值bi&#xff0c;每杀死一匹狼i&#xff0c;受到的伤害等于i的攻击值和与i相邻的狼的附加攻击值。求杀死所有的狼受到的伤害的最小值。 dp[i][j]&#xff1a;杀死区间i~j的狼受到伤害的最小值。 初始化&#xff1a; a[0]a[n1]…

洛谷P5115 : SAM + 边分治 + 虚树dp

题意 给出串 S S S&#xff0c; K 1 , K 2 K1,K2 K1,K2&#xff0c;求 ∑ 1 ≤ i < j ≤ n L C P ( i , j ) ⋅ L C S ( i , j ) ⋅ [ L C P ( i , j ) ≤ K 1 ] ⋅ [ L C S ( i , j ) ≤ K 2 ] \sum_{1 \le i < j \le n}{LCP(i,j) \cdot LCS(i,j) \cdot [LCP(i,j) \le…