Modèles de conception couramment utilisés en Java : 1. Mode Singleton ; 2. Mode Usine ; 3. Mode Constructeur ; 5. Mode Adaptateur ;
L'environnement d'exploitation de ce tutoriel : système Windows 7, version Java 8, ordinateur DELL G3.
Concept de base : S'assurer qu'une classe n'a qu'une seule instance et fournir un point d'accès global pour y accéder.
Écriture courante :
Style chinois affamé
public class Singleton{ private static Singleton singleton = new Singleton(); private Singleton(){ } public static Singleton getInstance(){ return singleton; } }
Appel
Singleton.getInstance().method();
Style paresseux
public class Singleton { /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */ private static Singleton instance = null; /* 私有构造方法,防止被实例化 */ private Singleton() {} /* 1:懒汉式,静态工程方法,创建实例 */ public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } protected void method() { System.out.println("SingletonInner"); } }
Appel :
Singleton.getInstance().method();
Avantages : Chargement paresseux (chargement uniquement en cas de besoin), adapté à un fonctionnement monothread
Inconvénients : Thread dangereux, il est facile d'être désynchronisé en multi- threads , par exemple lorsque des opérations de lecture et d'écriture fréquentes sont effectuées sur des objets de base de données.
Mode de vérification de thread double
public class SingletonInner { private static volatile SingletonInner sInst = null; // <<< 这里添加了 volatile /** * 私有的构造函数 */ private SingletonInner() {} public static SingletonInner getInstance() { if (inst == null) { synchronized (SingletonInner.class) {if (inst == null) { sInst = new SingletonInner(); } } } return sInst; } protected void method() { System.out.println("SingletonInner"); } }
Appel :
Singleton.getInstance().method();
Avantages : sécurité des threads, prise en charge du chargement retardé, efficacité d'appel élevée
Inconvénients : le la méthode d'écriture est compliquée et pas concise
Implémentation de la classe interne
public class SingletonInner { /** * 内部类实现单例模式 * 延迟加载,减少内存开销 */ private static class SingletonHolder { private static SingletonInner instance = new SingletonInner(); } /** * 私有的构造函数 */ private SingletonInner() {} public static SingletonInner getInstance() { return SingletonHolder.instance; } protected void method() { System.out.println("SingletonInner"); } }
Appel :
Singleton.getInstance().method();
Avantages : chargement paresseux, sécurité des threads (lors du chargement de la classe en java Mutuellement exclusif), réduit également la consommation de mémoire, il est recommandé d'utiliser la méthode de classe interne.
Concept de base : Fournir une interface de transition pour la création d'objets, de manière à protéger et à isoler le processus spécifique de création d'objets afin d'atteindre l'objectif d'amélioration de la flexibilité.
est divisé en trois catégories :
Mode usine simple Simple Factory
: peu propice à la production de séries de produits
: Également connu sous le nom de Polymorphic Factory Factory Method
: Également connu sous le nom de boîte à outils, il génère des familles de produits, mais n'est pas propice à la génération de nouvelles les produits Abstract Factory
Modèle d'usine simple
Le modèle d'usine simple est également appeléModèle de méthode d'usine statique. Il ressort du changement de nom que ce mode doit être très simple. Son but est simple : définir une interface de création d'objets.
Dans le modèle d'usine simple, une classe d'usine est au centre de l'appel à l'instanciation de classe de produit. Elle détermine quelle classe de produit doit être instanciée, tout comme un agent de la circulation se tenant dans le flux de véhicules qui passent. revient à décider dans quelle direction laisser circuler les véhicules. Jetons d'abord un coup d'œil à sa composition :public class Factory{ //getClass 产生Sample 一般可使用动态类装载装入类。 public static Sample creator(int which){ if (which==1) return new SampleA(); else if (which==2) return new SampleB(); } }Une autre norme populaire consiste à nommer la méthode de fabrique statique
ou valueOf
. getInstance
: L'instance renvoyée par cette méthode a la même valeur que son paramètre, par exemple : valueOf
Integer a=Integer.valueOf(100); //返回取值为100的Integer对象
public class Complex { private final float re; private final float im; private Complex(float re, float im){ this.re = re; this.im = im; } public static Complex valueOf(float re, float im){ return new Complex(re, im); } public static Complex valueOfPolar(float r, float theta){ return new Complex((float)(r * Math.cos(theta)), (float)(r * Math.sin(theta))); } }Comme le montre le code ci-dessus, la méthode valueOf() peut effectuer opérations de conversion de type, dans cet exemple, les données de base de type int sont converties en un objet Integer.
: L'instance renvoyée correspond aux paramètres, par exemple : getInstance
Calendar cal=Calendar.getInstance(Locale.CHINA); //返回符合中国标准的日历
Modèle de méthode d'usine
Le modèle de méthode d'usine est une étape supplémentaire en avant du modèle d'usine simple Abstraction et généralisation, dans le modèle de méthode d'usine, ce n'est plus seulement une classe d'usine qui détermine quelle classe de produit doit être instanciée. Cette décision est laissée à la sous-classe de l'usine abstraite.Regardons sa composition :
工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的“上帝类”。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活 起来——当有新的产品(即暴发户的汽车)产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有的代 码。可以看出工厂角色的结构也是符合开闭原则的!
示例代码:
//抽象产品角色 public interface Moveable { void run(); } //具体产品角色 public class Plane implements Moveable { @Override public void run() { System.out.println("plane...."); } } //具体产品角色 public class Broom implements Moveable { @Override public void run() { System.out.println("broom....."); } } //抽象工厂 public abstract class VehicleFactory { abstract Moveable create(); } //具体工厂 public class PlaneFactory extends VehicleFactory{ public Moveable create() { return new Plane(); } } //具体工厂 public class BroomFactory extends VehicleFactory{ public Moveable create() { return new Broom(); } } //测试类 public class Test { public static void main(String[] args) { VehicleFactory factory = new BroomFactory(); Moveable m = factory.create(); m.run(); } }
可以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。因为如果不能避免这种情 况,可以考虑使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实 现。
简单工厂和工厂方法模式的比较
工厂方法模式和简单工厂模式在定义上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类,而不像简单工厂模式, 把核心放在一个实类上。工厂方法模式可以允许很多实的工厂类从抽象工厂类继承下来, 从而可以在实际上成为多个简单工厂模式的综合,从而推广了简单工厂模式。
反过来讲,简单工厂模式是由工厂方法模式退化而来。设想如果我们非常确定一个系统只需要一个实的工厂类, 那么就不妨把抽象工厂类合并到实的工厂类中去。而这样一来,我们就退化到简单工厂模式了。
抽象工厂模式
示例代码:
//抽象工厂类 public abstract class AbstractFactory { public abstract Vehicle createVehicle(); public abstract Weapon createWeapon(); public abstract Food createFood(); } //具体工厂类,其中Food,Vehicle,Weapon是抽象类, public class DefaultFactory extends AbstractFactory{ @Override public Food createFood() { return new Apple(); } @Override public Vehicle createVehicle() { return new Car(); } @Override public Weapon createWeapon() { return new AK47(); } } //测试类 public class Test { public static void main(String[] args) { AbstractFactory f = new DefaultFactory(); Vehicle v = f.createVehicle(); v.run(); Weapon w = f.createWeapon(); w.shoot(); Food a = f.createFood(); a.printName(); } }
在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。 在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式。
总结
简单工厂模式是由一个具体的类去创建其他类的实例,父类是相同的,父类是具体的。
工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。它针对的是有多个产品的等级结构。而工厂方法模式针对的是一个产品的等级结构。
基本概念:是一种对象构建的设计模式,它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
Quels sont les modèles de conception couramment utilisés en Java ?是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们。用户不知道内部的具体构建细节。Quels sont les modèles de conception couramment utilisés en Java ?是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到。
UML结构图:
上图是Strategy 模式的结构图,让我们可以进行更方便的描述:
Builder:为创建一个Product对象的各个部件指定抽象接口。
ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,提供一个检索产品的接口
Director:构造一个使用Builder接口的对象。
Product:表示被构造的复杂对象。ConcreateBuilder创建该产品的内部表示并定义它的装配过程。
为何使用
是为了将构建复杂对象的过程和它的部件解耦。注意:是解耦过程和部件。
因为一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件:车轮、方向盘、发动机,还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Quels sont les modèles de conception couramment utilisés en Java ?就是为了将部件和组装过程分开。
如何使用
首先假设一个复杂对象是由多个部件组成的,Quels sont les modèles de conception couramment utilisés en Java ?是把复杂对象的创建和部件的创建分别开来,分别用Builder类和Director类来表示。
首先,需要一个接口,它定义如何创建复杂对象的各个部件:
public interface Builder { //创建部件A 比如创建汽车车轮void buildPartA(); //创建部件B 比如创建汽车方向盘void buildPartB(); //创建部件C 比如创建汽车发动机void buildPartC(); //返回最后组装成品结果 (返回最后装配好的汽车) //成品的组装过程不在这里进行,而是转移到下面的Director类中进行. //从而实现了解耦过程和部件 Product getResult(); }
用Director构建最后的复杂对象,而在上面Builder接口中封装的是如何创建一个个部件(复杂对象是由这些部件组成的),也就是说Director的内容是如何将部件最后组装成成品:
public class Director { private Builder builder; public Director( Builder builder ) { this.builder = builder; } // 将部件partA partB partC最后组成复杂对象 //这里是将车轮 方向盘和发动机组装成汽车的过程 public void construct() { builder.buildPartA(); builder.buildPartB(); builder.buildPartC(); } }
Builder的具体实现ConcreteBuilder:
public class ConcreteBuilder implements Builder { Part partA, partB, partC; public void buildPartA() { //这里是具体如何构建 } public void buildPartB() { //这里是具体如何构建 } public void buildPartC() { //这里是具体如何构建 } public Product getResult() { //返回最后组装成品结果 } }
复杂对象:产品Product:
public interface Product { }
复杂对象的部件:
public interface Part { }
我们看看如何调用Quels sont les modèles de conception couramment utilisés en Java ?:
ConcreteBuilder builder = new ConcreteBuilder(); Director director = new Director( builder ); director.construct(); Product product = builder.getResult();
Quels sont les modèles de conception couramment utilisés en Java ?的应用
在Java实际使用中,我们经常用到"池"(Pool)的概念,当资源提供者无法提供足够的资源,并且这些资源需要被很多用户反复共享时,就需要使用池。"池"实际是一段内存,当池中有一些复杂的资源的"断肢"(比如数据库的连接池,也许有时一个连接会中断),如果循环再利用这些"断肢",将提高内存使用效率,提高池的性能。修改Quels sont les modèles de conception couramment utilisés en Java ?中Director类使之能诊断"断肢"断在哪个部件上,再修复这个部件。
基本概念:Quels sont les modèles de conception couramment utilisés en Java ?定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。Quels sont les modèles de conception couramment utilisés en Java ?又叫发布-订阅(Publish/Subscribe)模式。
UML结构图
上图是Observer 模式的结构图,让我们可以进行更方便的描述:
Subject类:它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察着。抽象主题提供一个接口,可以增加和删除观察着对象。
Observer类:抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。
ConcreteSubject类:具体主题,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
ConcreteObserver类:具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。
如何使用
例如:老师有电话号码,学生需要知道老师的电话号码以便于在合适的时候拨打,在这样的组合中,老师就是一个被观察者(Subject),学生就是需要知道信息的观察者,当老师的电话号码发生改变时,学生得到通知,并更新相应的电话记录。
先创建一个Subject类:
/** * Subject(目标,Subject): * 目标知道它的观察者。可以有任意多个观察者观察同一个目标。 * 提供注册和删除观察者对象的接口。 */ public interface Subject { public void attach(Observer mObserver); public void detach(Observer mObserver); public void notice(); }
创建Observer类:
/** * Observer(观察者,Observer): * 为那些在目标发生改变时需要获得通知的对象定义一个更新接口。 */ public interface Observer { public void update(); }
创建ConcreteSubject类:
/** * ConcreteSubject(具体目标,Teacher) * 将有关状态存入各ConcreteObserve对象。 * 当他的状态发生改变时,向他的各个观察者发出通知。 */ public class Teacher implements Subject{ private String phone; private Vector students; public Teacher(){ phone = ""; students = new Vector(); } @Override public void attach(Observer mObserver) { students.add(mObserver); } @Override public void detach(Observer mObserver) { students.remove(mObserver); } @Override public void notice() { for(int i=0;i<students.size();i++){ ((Observer)students.get(i)).update(); } } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; notice(); } }
创建ConcreteObserver类:
/** * ConcreteObserver(具体观察者, Student): * 维护一个指向ConcreteSubject对象的引用。 * 存储有关状态,这些状态应与目标的状态保持一致。 * 实现Observer的更新接口以使自身状态与目标的状态保持一致。 */ public class Student implements Observer{ private String name; private String phone; private Teacher mTeacher; public Student(String name,Teacher t){ this.name = name; mTeacher = t; } public void show(){ System.out.println("Name:"+name+"\nTeacher'sphone:" + phone); } @Override public void update() { phone = mTeacher.getPhone(); } }
客户端测试:
/** * 观察者(Observer)模式测试类 */ public class ObserverClient { public static void main(String[] args) { Vector students = new Vector(); Teacher t = new Teacher(); for(int i= 0;i<10;i++){ Student st = new Student("Andy.Chen"+i,t); students.add(st); t.attach(st); } System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Observer Patterns." +"\n" +"-------------------------------"); t.setPhone("12345678"); for(int i=0;i<3;i++) ((Student)students.get(i)).show(); t.setPhone("87654321"); for(int i=0;i<3;i++) ((Student)students.get(i)).show(); } }
程序运行结果如下:
Welcome to Andy.Chen Blog! Observer Patterns. ------------------------------- Name:Andy.Chen0 Teacher'sphone:12345678 Name:Andy.Chen1 Teacher'sphone:12345678 Name:Andy.Chen2 Teacher'sphone:12345678 Name:Andy.Chen0 Teacher'sphone:87654321 Name:Andy.Chen1 Teacher'sphone:87654321 Name:Andy.Chen2 Teacher'sphone:87654321
总结
Quels sont les modèles de conception couramment utilisés en Java ?何时适用?
当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中可以使他们各自独立地改变和复用。
当对一个对象的改变需要同时改变其它对象,而不知道具体由多少对象有待改变。
当一个对象必须通知其他对象,而它又不能假定其他对象是谁,换言之,你不希望这些对象是紧密耦合的。让耦合的双方都依赖于抽象,而不是依赖于具体。
基本概念:适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
适配器模式的用途
用电器做例子,笔记本电脑的插头一般都是三相的,即除了阳极、阴极外,还有一个地极。而有些地方的电源插座却只有两极,没有地极。电源插座与笔记本电脑的电源插头不匹配使得笔记本电脑无法使用。这时候一个三相到两相的转换器(适配器)就能解决此问题,而这正像是本模式所做的事情。
适配器模式的结构
适配器模式有类的适配器模式
和对象的适配器模式
两种不同的形式。
在上图中可以看出,Adaptee类并没有sampleOperation2()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,提供一个中间环节,即类Adapter,把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是继承关系,这决定了这个适配器模式是类的:
从上图可以看出,Adaptee类并没有sampleOperation2()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,需要提供一个包装(Wrapper)类Adapter。这个包装类包装了一个Adaptee的实例,从而此包装类能够把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是委派关系,这决定了适配器模式是对象的。
示例代码:
public interface Target { /** * 这是源类Adaptee也有的方法 */ public void sampleOperation1(); /** * 这是源类Adapteee没有的方法 */ public void sampleOperation2(); }public class Adaptee { public void sampleOperation1(){} }
适配器类:
public class Adapter { private Adaptee adaptee; public Adapter(Adaptee adaptee){ this.adaptee = adaptee; } /** * 源类Adaptee有方法sampleOperation1 * 因此适配器类直接委派即可 */ public void sampleOperation1(){ this.adaptee.sampleOperation1(); } /** * 源类Adaptee没有方法sampleOperation2 * 因此由适配器类需要补充此方法 */ public void sampleOperation2(){ //写相关的代码 } }
类适配器和Quels sont les modèles de conception couramment utilisés en Java ?的权衡
对于类适配器由于适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的关系,当适配器继承了Adaptee后,就不可能再去处理 Adaptee的子类了。
对于Quels sont les modèles de conception couramment utilisés en Java ?一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为Quels sont les modèles de conception couramment utilisés en Java ?采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。
对于类适配器适配器可以重定义Adaptee的部分行为,相当于子类覆盖父类的部分实现方法。
对于Quels sont les modèles de conception couramment utilisés en Java ?要重定义Adaptee的行为比较困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器组合子类。虽然重定义Adaptee的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。
对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。
对于Quels sont les modèles de conception couramment utilisés en Java ?,需要额外的引用来间接得到Adaptee。
建议尽量使用Quels sont les modèles de conception couramment utilisés en Java ?的实现方式,多用合成或聚合、少用继承。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。
适配器模式的优点
更好的复用性:系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
更好的扩展性:在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
适配器模式的缺点
过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
基本概念:为其他对象提供一种代理以控制对这个对象的访问。也可以说,在出发点到目的地之间有一道中间层,意为代理。
为什么要使用
授权机制不同级别的用户对同一对象拥有不同的访问权利,如在论坛系统中,就使用Proxy进行授权机制控制,访问论坛有两种人:注册用户和游客(未注册用户),论坛就通过类似ForumProxy这样的代理来控制这两种用户对论坛的访问权限。
某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动。
举例两个具体情况:
总之原则是,对于开销很大的对象,只有在使用它时才创建,这个原则可以为我们节省很多宝贵的Java内存。所以,有些人认为Java耗费资源内存,我以为这和程序编制思路也有一定的关系。
如何使用
以论坛系统为例,访问论坛系统的用户有多种类型:注册普通用户、论坛管理者、系统管理者、游客。注册普通用户才能发言,论坛管理者可以管理他被授权的论坛,系统管理者可以管理所有事务等,这些权限划分和管理是使用Proxy完成的。
在Forum中陈列了有关论坛操作的主要行为,如论坛名称,论坛描述的获取和修改,帖子发表删除编辑等,在ForumPermissions中定义了各种级别权限的用户:
public class ForumPermissions implements Cacheable { /** * Permission to read object. */ public static final int READ = 0; /** * Permission to administer the entire sytem. */ public static final int SYSTEM_ADMIN = 1; /** * Permission to administer a particular forum. */ public static final int FORUM_ADMIN = 2; /** * Permission to administer a particular user. */ public static final int USER_ADMIN = 3; /** * Permission to administer a particular group. */ public static final int GROUP_ADMIN = 4; /** * Permission to moderate threads. */ public static final int MODERATE_THREADS = 5; /** * Permission to create a new thread. */ public static final int CREATE_THREAD = 6; /** * Permission to create a new message. */ public static final int CREATE_MESSAGE = 7; /** * Permission to moderate messages. */ public static final int MODERATE_MESSAGES = 8; public boolean isSystemOrForumAdmin() { return (values[FORUM_ADMIN] || values[SYSTEM_ADMIN]); } //相关操作代码 }
因此,Forum中各种操作权限是和ForumPermissions定义的用户级别有关系的,作为接口Forum的实现:ForumProxy正是将这种对应关系联系起来。比如,修改Forum的名称,只有论坛管理者或系统管理者可以修改,代码如下:
public class ForumProxy implements Forum { private ForumPermissions permissions; private Forum forum; this.authorization = authorization; public ForumProxy(Forum forum, Authorization authorization,ForumPermissions permissions){ this.forum = forum; this.authorization = authorization; this.permissions = permissions; } ..... public void setName(String name) throws UnauthorizedException, ForumAlreadyExistsException{ //只有是系统或论坛管理者才可以修改名称 if (permissions.isSystemOrForumAdmin()) { forum.setName(name); } else { throw new UnauthorizedException(); } } ... }
而DbForum才是接口Forum的真正实现,以修改论坛名称为例:
public class DbForum implements Forum, Cacheable { ... public void setName(String name) throws ForumAlreadyExistsException { .... this.name = name; //这里真正将新名称保存到数据库中 saveToDb(); .... } ... }
凡是涉及到对论坛名称修改这一事件,其他程序都首先得和ForumProxy打交道,由ForumProxy决定是否有权限做某一样事情,ForumProxy是个名副其实的"网关","安全代理系统"。
在平时应用中,无可避免总要涉及到系统的授权或安全体系,不管你有无意识的使用Proxy,实际你已经在使用Proxy了。
流程图
基本概念:Quels sont les modèles de conception couramment utilisés en Java ?(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,Quels sont les modèles de conception couramment utilisés en Java ?比生成子类更为灵活。
UML结构图
上图是Decorator 模式的结构图,让我们可以进行更方便的描述:
Component
是定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent
是定义了一个具体的对象,也可以给这个对象添加一些职责。
Decorator是装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator存在的。ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能。
如何使用
假设情景:某人装扮自己形象,穿衣服,裤子,鞋子,戴帽子等来把自己给包装起来,需要把所需的功能按正确的顺序串联起来进行控制,我们应该如何设计才能做到呢?如下,先看下代码结构图:
先创建一个接口类:Component.java
public interface Component { void show(); }
创建一个具体的 ConcreteComponent 来实现 Component 接口:Person.java
public class Person implements Component{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Person(String name){ this.name = name; } @Override public void show() { System.out.println("装扮的" + name); } }
创建装饰类 Decorator 实现 Component 接口
public class Decorator implements Component{ private Component mComponent; public void decoratorObj(Component component){ mComponent = component; } @Override public void show() { if(mComponent != null){ mComponent.show(); } } }
分别创建具体的装饰类:Jeans.java , Pelisse.java, Sandal.java ...等等,分别继承 Decorator.java 类
/** 牛仔裤 */ public class Jeans extends Decorator { @Override public void show(){ System.out.println("穿牛仔裤"); super.show(); } }
客户端测试类
/** * Quels sont les modèles de conception couramment utilisés en Java ?测试客户端 */ public class DecoratorClient { public static void main(String[] args) { System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Decorator Patterns." +"\n"); Person mPerson = new Person("Andy"); Sandal mSandal = new Sandal(); Jeans mJeans = new Jeans(); TShirt mShirt = new TShirt(); mShirt.decoratorObj(mPerson); mJeans.decoratorObj(mShirt); mSandal.decoratorObj(mJeans); mSandal.show(); } }
测试结果
Welcome to Andy.Chen Blog! Decorator Patterns. 穿凉鞋 穿牛仔裤 穿T-Shirt 装扮的Andy
总结
Decorator
模式有以下的优缺点:
Plus flexible que l'héritage statique. Par rapport à l'héritage statique des objets, le modèle Decorator offre un moyen plus flexible d'ajouter des responsabilités aux objets. Vous pouvez utiliser les méthodes d'ajout et de détachement et utiliser la décoration pour ajouter. responsabilités au moment de l’exécution et supprimer les responsabilités. L'utilisation du mécanisme d'héritage pour ajouter des responsabilités nécessite la création d'une nouvelle sous-classe. Si vous devez ajouter des fonctions à toutes les sous-classes d'origine, chaque sous-classe doit être réécrite, ce qui augmente la complexité du système. De plus, plusieurs sous-classes peuvent être fournies pour un composant spécifique. classe Décorateur, ce type de mélange et de correspondance est difficile à réaliser en utilisant l'héritage.
Évitez d'avoir trop de fonctionnalités dans les classes supérieures dans la hiérarchie. Le modèle Décorateur propose une approche « par répartition » pour ajouter des responsabilités sans essayer de créer un complexe. La classe personnalisable prend en charge toutes les fonctionnalités prévisibles. Au lieu de cela, vous pouvez définir une classe simple et y ajouter progressivement des fonctions à l'aide de la classe Decorator, combinant des composants simples pour créer des fonctions complexes.
Le décorateur est différent de son composant. Le décorateur est un package transparent Si nous partons du point de vue de l'identification de l'objet, il y a une différence entre un composant décoré et ce composant, les décorateurs devraient donc le faire. ne pas être utilisé avec des identifiants d'objet.
Produit de nombreux petits objets. La conception du système utilisant le modèle Decorator produit souvent de nombreux petits objets qui se ressemblent. Ces objets ne diffèrent que par la façon dont ils sont connectés les uns aux autres.
Tutoriels vidéo associés recommandés : Tutoriel vidéo Java
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!