Java 设计模式,是一套由前人总结的,被反复使用的代码设计经验。它为我们解决一些实际问题提供了一些很好的设计模板,了解设计模式,有利于提高我们的代码设计能力,架构能力,更有可能自己能够设计出适合业务的一套设计模式。接下来就让我们了解一下这些神秘的设计模式。
总的来说,设计模式可以分为以下几大类。
创建型模式:
属于创建型模式的设计模式有单例模式,简单工厂模式,工厂方法模式,抽象工厂模式,原型模式,建造者模式。这些模式都是用来创建对象的,提供了方便的接口给客户端使用。创建型模式将对象的创建和使用分离,在使用对象时无需关心对象的创建细节,从而降低系统的耦合度,让设计方案更易于修改和扩展。
结构型模式:
属于结构型模式的设计模式有适配器模式,桥接模式,组合模式,装饰模式,外观模式,享元模式,代理模式。他们大多是用来组织某种结构,提供某种复杂的功能,不同的结构型模式关注如何将现有类或对象组织在一起形成更加强大的结构。比如适配器提供了将两个毫不相关的类或接口提供了桥梁,组合模式为树形结构提供了模板,外观模式为客户提供了方便简洁的接口,并且隐藏了内部细节。
行为型模式:
属于行为型模式的设计模式有职责链模式,命令模式,解释器模式,迭代器模式,中介者模式,备忘录模式,观察者模式,状态模式,策略模式,模板方法模式,访问者模式。这些模式为客户端提供了许多功能需求,注重对象之间的交互,关注他们之间的相互作用和职责划分。比如策略模式为用户提供了不同的策略选择,观察者模式,使得某个事件发生过后可以自动触发其他事件。又比如备忘录模式,使得用户可以恢复之前的某个状态,实现还原的功能。
接下来我们一个一个来看这些设计模式。
创造型模式
单例模式
需求:系统需要有一个恒定不变的对象,每次获取都是同一个对象。比如一个朝代只能有一个皇帝,一个公司只能有一个老板。那我们的系统就不能 new 出两个老板对象出来。windows 系统的任务管理器便是实现了单例模式。
特点:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。而单例模式又分为两类,懒汉式和饿汉式。
饿汉式单例即一开始就 new 出一个实例,始终占据着内存,不管他以后是不是被用到。优点是不用考虑多线程的问题。
代码如下
- public class EagerSingleton {
- privatestatic final EagerSingleton instance=new EagerSingleton();
- privateEagerSingleton(){}
- publicstatic EagerSingleton getInstance(){
- returninstance;
- }
- }
懒汉式单例即等到该对象第一次被使用时才 new 出一个实例,避免了一开始就占用资源。缺点是考虑多线程情况下同时 new 出多个实例的问题,需要使用锁,在多线程并发访问环境下会导致性能下降。
代码如下
- public class LazySingleton {
- privatestatic LazySingleton instance=null;
- private LazySingleton(){}
- synchronizedpublic static LazySingleton getInsatance(){
- if(instance==null)instance =new LazySingleton();
- returninstance;
- }
- }
IoDH(Initializationon Demand Holder): 即有延迟加载,又保证线程安全,不影响系统性能。缺点是与编程语言本身特性相关,java 中可以实现,但很多面向对象语言不支持。
- public class IoDH {
- privateIoDH(){}
- privatestatic class HolderClass{
- privatefinal static IoDH instance =new IoDH();
- }
- publicstatic IoDH getInsatance(){
- returnHolderClass.instance;
- }
- }
简单工厂模式
需求:客户端想简单地传入不同参数就得到不同类的实例,不想很麻烦地一个一个去 new。
特点:简单工厂针对抽象编程,使用 static 方法,根据传入的参数,new 出对应的具体子类,用户可以通过 Factory.Create("对应参数") 得到对应实例,免除创建的职责。缺点是工厂类集中了大量创建代码,一旦不能正常工作整个系统都要受到影响。并且扩展十分困难,需要修改工厂逻辑。
示例代码如下:
- abstract class Product {
- public void methodSame(){
- //公共方法
- }
- //抽象业务方法
- public abstract void methodDiff();
- }
- public class ConcreteProduct1 extends Product{
- @Override
- public void methodDiff() {
- // TODO Auto-generated method stub
- System.out.println("我是第一号产品");
- }
- }
- public class ConcreteProduct2 extends Product{
- @Override
- public void methodDiff() {
- // TODO Auto-generated method stub
- System.out.println("我是第二号产品");
- }
- }
- public class Factory {
- public static Product getProduct(String arg){
- Product product =null;
- if(arg.equalsIgnoreCase("1")){
- product=new ConcreteProduct1();
- }else if(arg.equalsIgnoreCase("2")){
- product=new ConcreteProduct2();
- }
- return product;
- }
- }
- public class Client {
- public static void main(String[] args){
- Product product1,product2;
- product1=Factory.getProduct("1");
- product2=Factory.getProduct("2");
- product1.methodDiff();
- product2.methodDiff();
- }
- }
工厂方法模式:
需求:同简单工厂模式。
特点:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。在简单工厂模式中,有加入新产品需要修改工厂逻辑的缺点,在工厂方法模式中,我们不再使用同一个工厂,而是不同的产品提供不同的工厂,每当我们加入新产品时,只需要实现新的工厂类,不用改变原来的任何一处代码。它缺点是新增一个产品类还要提供与之对应的工厂类,个数将成对增加。
代码结构:抽象产品类(或接口)作为抽象工厂类(或接口)的创建方法的参数传入,再通过具体的工厂实现类来得到对应的具体产品类。
抽象工厂模式:
需求:在日常生活中,每个产品都有不同的牌子,而同一个牌子旗下可能有不同的产品,怎么去模拟这样一个模式?
特点: 在简单工厂模式的基础上新增两个概念,分别是产品族和产品等级结构。产品等级结构即产品的继承结构,即抽象产品类 / 接口与它的实现类。产品族可以理解为一个品牌,相同品牌旗下的不同产品构成一个产品族。在抽象工厂模式中,一个产品族是由同一个工厂生产的。增加新的产品族只需要增加一个新的工厂,无需修改已有系统,符合开闭原则。但是,增加新的产品等级结构却需要较大的修改,这是它的缺点。
示例代码如下:
- public interface ComboBox {
- publicvoid display();
- }
- public interface TextField {
- publicvoid display();
- }
- public interface Button {
- publicvoid display();
- }
- public class SpringButton implementsButton{
- @Override
- publicvoid display() {
- //TODO Auto-generated method stub
- System.out.println("显示浅绿色按钮");
- }
- }
- public class SpringComboBox implementsComboBox{
- @Override
- publicvoid display() {
- //TODO Auto-generated method stub
- System.out.println("显示绿色边框组合框");
- }
- }
- public class SpringTextField implementsTextField{
- @Override
- publicvoid display() {
- //TODO Auto-generated method stub
- System.out.println("显示绿色边框文本框。");
- }
- }
- public class SummerButton implementsButton{
- @Override
- publicvoid display() {
- //TODO Auto-generated method stub
- System.out.println("显示浅蓝色按钮");
- }
- }
- public class SummerComboBox implementsComboBox{
- @Override
- publicvoid display() {
- //TODO Auto-generated method stub
- System.out.println("显示蓝色边框组合框");
- }
- }
- public class SummerTextField implementsTextField{
- @Override
- publicvoid display() {
- //TODO Auto-generated method stub
- System.out.println("显示蓝色边框文本框");
- }
- }
- public interface SkinFactory {
- publicButton createButton();
- publicTextField createTextField();
- publicComboBox createComboBox();
- }
- public class SpringSkinFactory implementsSkinFactory{
- @Override
- publicButton createButton() {
- //TODO Auto-generated method stub
- returnnew SpringButton();
- }
- @Override
- publicTextField createTextField() {
- //TODO Auto-generated method stub
- returnnew SpringTextField();
- }
- @Override
- publicComboBox createComboBox() {
- //TODO Auto-generated method stub
- returnnew SpringComboBox();
- }
- }
- public class SummerSkinFactory implementsSkinFactory{
- @Override
- publicButton createButton() {
- //TODO Auto-generated method stub
- returnnew SummerButton();
- }
- @Override
- publicTextField createTextField() {
- //TODO Auto-generated method stub
- returnnew SummerTextField();
- }
- @Override
- publicComboBox createComboBox() {
- //TODO Auto-generated method stub
- returnnew SummerComboBox();
- }
- }
原型模式
需求:创建一个复杂对象比较麻烦或者成本较高,需要一个方法来简化对象创建的过程。或者是需要保存对象的状态。
特点:让对象实现 Cloneable 接口,重写 Clone 方法。适用 clone 方法需要注意的是,克隆分为浅克隆和深克隆。当一个对象中所含的数据都是基本类型数据时,调用 clone 方法不会有任何问题,这是所谓的浅克隆。当你数据中包含引用类型时,简单地调用 clone 方法只是会对象的引用复制过去,而不是真正的复制一个对象。以电视(对象)和遥控器(引用)为例,我们只是复制了一个遥控器,而没有复制电视。这样我们在对其修改的时候,克隆后的对象会改变被克隆对象的状态。而深克隆就是需要我们将电视也克隆一份,需要在类的 clone 方法中对引用的类也调用 clone 方法,或者直接用 serializable 将整个类序列化写入对象流中再读取出来。
浅克隆示例代码:
- public class WeeklyLog implementsCloneable{
- privateString name;
- privateString date;
- privateString content;
- publicvoid setName(String name){
- this.name=name;
- }
- publicvoid setDate(String date){
- this.date=date;
- }
- publicvoid setContent(String content){
- this.content=content;
- }
- publicString getName(){
- returnthis.name;
- }
- publicString getDate(){
- returnthis.date;
- }
- publicString getContent(){
- returnthis.content;
- }
- publicWeeklyLog clone(){
- Objectobj=null;
- try{
- obj=super.clone();
- return(WeeklyLog)obj;
- }
- catch(Exception e) {
- //TODO: handle exception
- System.out.println("不支持复制!");
- returnnull;
- }
- }
- publicstatic void main(String[] args){
- WeeklyLoglog =new WeeklyLog();
- log.setName("小明");
- log.setDate("第1周");
- log.setContent("每天加班");
- System.out.println("****周报****");
- System.out.println("周次:"+log.getDate());
- System.out.println("姓名:"+log.getName());
- System.out.println("内容:"+log.getContent());
- System.out.println("************");
- WeeklyLoglog2 =log.clone();
- log2.setDate("第2周");
- System.out.println("****周报****");
- System.out.println("周次:"+log2.getDate());
- System.out.println("姓名:"+log2.getName());
- System.out.println("内容:"+log2.getContent());
- System.out.println("************");
- }
- }
深克隆示例代码:
- public class Attachment implementsSerializable {
- privateString name;
- publicvoid setName(String name) {
- this.name = name;
- }
- publicString getName() {
- returnname;
- }
- publicvoid download() {
- System.out.println("下载附件:文件名为" + name);
- }
- }
- public class WeeklyLog implementsCloneable,
- Serializable {
- privateString name;
- privateString date;
- privateString content;
- privateAttachment attachment;
- publicvoid setAttachment(Attachment attachment) {
- this.attachment = attachment;
- }
- publicvoid setName(String name) {
- this.name = name;
- }
- publicvoid setDate(String date) {
- this.date = date;
- }
- publicvoid setContent(String content) {
- this.content = content;
- }
- publicString getName() {
- returnthis.name;
- }
- publicString getDate() {
- returnthis.date;
- }
- publicString getContent() {
- returnthis.content;
- }
- publicAttachment getAttachment() {
- returnthis.attachment;
- }
- publicWeeklyLog clone() {
- Objectobj = null;
- try {
- obj = super.clone();
- return (WeeklyLog) obj;
- } catch(Exception e) {
- //TODO: handle exception
- System.out.println("不支持复制!");
- returnnull;
- }
- }
- publicWeeklyLog deepClone() throws IOException,
- ClassNotFoundException {
- ByteArrayOutputStreambao = new ByteArrayOutputStream();
- ObjectOutputStreamoos = new ObjectOutputStream(bao);
- oos.writeObject(this);
- ByteArrayInputStreambis = new ByteArrayInputStream(bao.toByteArray());
- ObjectInputStreamois = new ObjectInputStream(bis);
- return (WeeklyLog) ois.readObject();
- }
- publicstatic void main(String[] args) throws ClassNotFoundException,
- IOException {
- WeeklyLoglog = new WeeklyLog();
- Attachmentattachment = new Attachment();
- log.setAttachment(attachment);
- log.setName("小明");
- log.setDate("第1周");
- log.setContent("每天加班");
- System.out.println("****周报****");
- System.out.println("周次:" + log.getDate());
- System.out.println("姓名:" + log.getName());
- System.out.println("内容:" + log.getContent());
- System.out.println("************");
- WeeklyLoglog2;
- log2 = log.deepClone();
- log2.setDate("第2周");
- System.out.println("****周报****");
- System.out.println("周次:" + log2.getDate());
- System.out.println("姓名:" + log2.getName());
- System.out.println("内容:" + log2.getContent());
- System.out.println("************");
- System.out.println("附件是否相同:" + (log.getAttachment() == log2.getAttachment()));
- }
- }
建造者模式:
需求:我(客户)想要造一辆车,但我不懂内部构造。
特点:将一个复杂对象的构建与它的表示分离,使得同样的构件过程可以创建不同的表示。客户端只需要实例化指挥者类,根据传入的具体的建造者类型,指挥者一步一步地构造一个完整的产品,相同的构造过程可以创造完全不同的产品。核心在于如何一步一步地构建一个包含多个组成部件的完整对象,使用相同的构造过程构造不同的产品。
优点:是用户完全不必要知道细节,并且可以方便地替换具体建造者或者增加建造者,符合开闭原则。所创造的产品必须是组成部分相似,如果很多组成部分不同则不适用。
适用场景:
①需要的生成对象有复杂的内部结构。
②需要生成的对象的属性相互依赖,需要指定其顺序。
③对象的创建过程独立于创建该对象的类。
④隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
示例代码:
- public class Actor {
- privateString type;
- privateString sex;
- privateString face;
- privateString costume;
- privateString hairStyle;
- publicvoid setType(String type){
- this.type=type;
- }
- publicvoid setSex(String sex){
- this.sex=sex;
- }
- publicvoid setFace(String face){
- this.face=face;
- }
- publicvoid setCostume(String costume){
- this.costume=costume;
- }
- publicvoid setHairStyle(String hairStyle){
- this.hairStyle=hairStyle;
- }
- publicString getType(){
- returntype;
- }
- publicString getSex(){
- returnsex;
- }
- publicString getFace(){
- returnface;
- }
- publicString getCostume(){
- return costume;
- }
- publicString getHairStyle(){
- returnhairStyle;
- }
- }
- abstract class ActorBuilder {
- protectedActor actor=new Actor();
- publicabstract void buildType();
- publicabstract void buildSex();
- publicabstract void buildFace();
- publicabstract void buildCostume();
- publicabstract void buildHairStyle();
- publicActor createActor(){
- returnactor;
- }
- }
- public class AngelBuilder extendsActorBuilder{
- publicvoid buildType(){
- actor.setType("天使");
- }
- publicvoid buildSex(){
- actor.setSex("女");
- }
- publicvoid buildFace(){
- actor.setFace("漂亮");
- }
- publicvoid buildCostume(){
- actor.setCostume("白裙");
- }
- publicvoid buildHairStyle(){
- actor.setHairStyle("披肩长发");
- }
- }
- public class HeroBuilder extendsActorBuilder{
- publicvoid buildType(){
- actor.setType("英雄");
- }
- publicvoid buildSex(){
- actor.setSex("男");
- }
- publicvoid buildFace(){
- actor.setFace("英俊");
- }
- publicvoid buildCostume(){
- actor.setCostume("盔甲");
- }
- publicvoid buildHairStyle(){
- actor.setHairStyle("飘逸");
- }
- }
- public class DevilBuilder extendsActorBuilder{
- publicvoid buildType(){
- actor.setType("恶魔");
- }
- publicvoid buildSex(){
- actor.setSex("妖");
- }
- publicvoid buildFace(){
- actor.setFace("丑陋");
- }
- publicvoid buildCostume(){
- actor.setCostume("黑衣");
- }
- publicvoid buildHairStyle(){
- actor.setHairStyle("光头");
- }
- }
- public class ActorController {
- publicActor construct(ActorBuilder ab){
- Actoractor;
- ab.buildType();
- ab.buildSex();
- ab.buildFace();
- ab.buildCostume();
- ab.buildHairStyle();
- actor=ab.createActor();
- returnactor;
- }
- }
- public class Client {
- publicstatic void main(String[] args){
- ActorBuilderab=new AngelBuilder();
- ActorControllerac=new ActorController();
- Actoractor;
- actor=ac.construct(ab);
- Stringtype=actor.getType();
- System.out.println(type+"的外观:");
- System.out.println("性别:"+actor.getSex());
- System.out.println("面容:"+actor.getFace());
- System.out.println("服装:"+actor.getCostume());
- System.out.println("发型:"+actor.getHairStyle());
- }
- }
适配器模式
需求:我们拿到了客户提供的类,但是它又不太符合我们系统的接口,如何兼容?
特点:将一个接口转换成客户希望的另一个接口,使接口不兼容的类可以一起工作。类似电源适配器的作用。它是为了解决已经在运行中的项目问题,而不是一种还处在开发阶段时候应该考虑的设计模式。
适配器模式又可以分为类适配器和对象适配器。类适配器模式采用的是多重继承的方式,对象适配器采用的是组合的方式
类适配器示例代码:
- public interface Target {
- //目标自己有的方法
- publicvoid request();
- }
- public class ConcreteTarget implementsTarget{
- @Override
- publicvoid request() {
- //TODO Auto-generated method stub
- System.out.println("Ifyou need and help,please call me!");
- }
- }
- public class Adaptee {
- publicvoid doSomething(){
- System.out.println("I'mkind of busy,leave me alone,pls");
- }
- }
- public class Adapter extends Adapteeimplements Target{
- @Override
- publicvoid request() {
- //TODO Auto-generated method stub
- super.doSomething();
- }
- }
- public class Client {
- publicstatic void main(String[] args){
- Target target=new ConcreteTarget();
- target.request();
- Targettarget2=new Adapter();
- target2.request();
- }
- }
对象适配器示例代码:
- public interface IUserInfo {
- StringgetUserName();
- StringgetHomeAddress();
- StringgetMobileNumber();
- StringgetOfficeTelNumber();
- StringgetJobPosition();
- StringgetHomeTelNumber();
- }
- public interface IOuterUserBaseInfo {
- publicMap getUserBaseInfo();
- }
- public interface IOuterUserOfficeInfo {
- publicMap getUserOfficeInfo();
- }
- public interface IOuterUserHomeInfo {
- publicMap getUserHomeInfo();
- }
- public class OuterUserBaseInfo implementsIOuterUserBaseInfo{
- @Override
- publicMap getUserBaseInfo() {
- //TODO Auto-generated method stub
- HashMapbaseInfoMap=new HashMap<>();
- baseInfoMap.put("userName","这个员工叫混世魔王");
- baseInfoMap.put("mobileNumber","这个员工电话是:……");
- returnbaseInfoMap;
- }
- }
- public class OuterUserHomeInfo implementsIOuterUserHomeInfo{
- @Override
- publicMap getUserHomeInfo() {
- //TODO Auto-generated method stub
- HashMaphomeInfo=new HashMap();
- homeInfo.put("homeTelNumber", "员工的家庭电话是……");
- homeInfo.put("homeAddress","员工的家庭地址是……");
- returnhomeInfo;
- }
- }
- public class OuterUserOfficeInfo implementsIOuterUserOfficeInfo{
- @Override
- publicMap getUserOfficeInfo() {
- //TODO Auto-generated method stub
- HashMapofficeInfo=new HashMap();
- officeInfo.put("jobPosition","这个人的职位是boss");
- officeInfo.put("officeTelNumber","员工的办公电话是……");
- returnofficeInfo;
- }
- }
- public class OuterUserInfo implementsIUserInfo{
- privateIOuterUserHomeInfo homeInfo=null;
- privateIOuterUserBaseInfo baseInfo=null;
- privateIOuterUserOfficeInfo officeInfo=null;
- privateMap baseMap=null;
- privateMap homeMap=null;
- privateMap officeMap=null;
- publicOuterUserInfo(IOuterUserBaseInfo _baseInfo,IOuterUserHomeInfo_homeInfo,IOuterUserOfficeInfo _officeInfo) {
- //TODO Auto-generated constructor stub
- this.baseInfo=_baseInfo;
- this.homeInfo=_homeInfo;
- this.officeInfo=_officeInfo;
- this.baseMap=this.baseInfo.getUserBaseInfo();
- this.homeMap=this.homeInfo.getUserHomeInfo();
- this.officeMap=this.officeInfo.getUserOfficeInfo();
- }
- @Override
- publicString getUserName() {
- //TODO Auto-generated method stub
- StringuserName =(String)this.baseMap.get("userName");
- System.out.println(userName);
- returnuserName;
- }
- @Override
- publicString getHomeAddress() {
- //TODO Auto-generated method stub
- StringhomeAddress =(String)this.homeMap.get("homeAddress");
- System.out.println(homeAddress);
- returnhomeAddress;
- }
- @Override
- publicString getMobileNumber() {
- //TODO Auto-generated method stub
- StringmobileNumber =(String)this.baseMap.get("mobileNumber");
- System.out.println(mobileNumber);
- returnmobileNumber;
- }
- @Override
- publicString getOfficeTelNumber() {
- //TODO Auto-generated method stub
- StringofficeTelNumber =(String)this.officeMap.get("officeTelNumber");
- System.out.println(officeTelNumber);
- returnofficeTelNumber;
- }
- @Override
- publicString getJobPosition() {
- //TODO Auto-generated method stub
- StringjobPosition =(String)this.officeMap.get("jobPosition");
- System.out.println(jobPosition);
- returnjobPosition;
- }
- @Override
- publicString getHomeTelNumber() {
- //TODO Auto-generated method stub
- StringhomeTelNumber =(String)this.homeMap.get("homeTelNumber");
- System.out.println(homeTelNumber);
- returnhomeTelNumber;
- }
- }
将 IUserInfo 接口和另外三个接口 IOuterBase/Home/OfficeInfo 结合起来。可以通过 OuterUserInfo 这个类实现 IUserInfo 接口,将 IOuterBase/Home/OfficeInfo 这三个接口的实现类组合起来。我们用接口的方法实现就可以调用传进来的那三个接口的数据来实现相同的功能。这种适配方式叫做对象适配器,它利用的是对象的组合关系,因此在设计时较为灵活。
桥梁模式:
需求:通常来说,不发生变化的代码可以通过继承来复用,但是我有一些代码不确定以后是否发生变化,应该怎么处理才能避免风险呢?
特点:将抽象部分与其实现部分分离,使他们都可以独立地变化。能够尽量地把变化的元素封装到最细,最小的逻辑单元中。它有优秀的扩充能力,可以增加实现也可以增加抽象,并且细节部分对用户是透明的。
示例代码:
- public interface Implementor {
- publicvoid doSomething();
- publicvoid doAnything();
- }
- public class ConcreteImplementor1implements Implementor{
- @Override
- publicvoid doSomething() {
- //TODO Auto-generated method stub
- System.out.println("doSomething");
- }
- @Override
- publicvoid doAnything() {
- //TODO Auto-generated method stub
- System.out.println("doAnything");
- }
- }
- public class ConcreteImplementor2 implements Implementor{
- @Override
- publicvoid doSomething() {
- }
- @Override
- publicvoid doAnything() {
- }
- }
- public class Abstraction {
- privateImplementor imp;
- publicAbstraction(Implementor imp){
- this.imp=imp;
- }
- publicvoid request(){
- this.imp.doSomething();
- }
- publicImplementor getImp(){
- returnimp;
- }
- }
- public class RefinedAbstraction extendsAbstraction{
- publicRefinedAbstraction(Implementor imp) {
- super(imp);
- //TODO Auto-generated constructor stub
- }
- @Override
- publicvoid request(){
- /*
- * 业务处理……
- */
- super.request();
- super.getImp().doAnything();
- }
- }
组合模式:
需求:生活中有许多树形结构的例子,比如公司里面有总经理,总经理管理着研发部经理,销售部经理,市场部经理等,各经理又管理着许多组长,组长手下又有许多员工…… 如何表示这种树形结构?
特点:组合多个对象形成树形结构以表示具有 "整体 - 部分" 关系的层次结构。组合模式又分为安全组合模式和透明组合模式
安全组合模式:Component 接口中没有声明任何用于管理成员对象的方法,而是在 Composite 类中声明并实现这些方法,这种做法是安全的,因为根本不向叶子节点提供这些方法。缺点是不够透明,客户端不能完全针对抽象编程,必须有区别的对待叶子和容器。JAVA AWT 中使用的组合模式就是安全组合模式。
示例代码如下:
- public class Component {
- //个体和整体都具有的共享
- publicvoid doSomething(){
- //编写业务逻辑
- System.out.println("doSomething");
- }
- }
- public class Composite extends Component{
- privateArrayList<Component> componentArrayList=new ArrayList<Component>();
- //增加一个叶子构件或树枝构件
- publicvoid add(Component component){
- this.componentArrayList.add(component);
- }
- //删除一个叶子构件或树枝构件
- publicvoid remove(Component component){
- this.componentArrayList.remove(component);
- }
- //获得分支下的所有叶子构件和树枝构件
- publicArrayList<Component> getChildren(){
- returnthis.componentArrayList;
- }
- }
- public class Leaf extends Component{
- //可以覆盖父类方法
- privateString name;
- publicLeaf(String name){
- this.name=name;
- }
- @Override
- publicvoid doSomething(){
- System.out.println(name+":doSomething");
- }
- }
- public class Client {
- publicstatic void main(String[] args){
- //创建一个根节点
- Compositeroot =new Composite();
- //创建树枝构件
- Compositebranch=new Composite();
- Compositebranch2=new Composite();
- //创建叶子节点
- Leafleaf1=new Leaf("1");
- Leafleaf2=new Leaf("2");
- Leafleaf3=new Leaf("3");
- root.add(branch);
- root.add(leaf2);
- branch.add(leaf1);
- branch.add(branch2);
- branch2.add(leaf3);
- display(root);
- }
- publicstatic void display(Composite root){
- for(Componentc:root.getChildren()){
- if(cinstanceof Leaf){
- c.doSomething();
- }else{
- display((Composite)c);
- }
- }
- }
- }
透明组合模式中,抽象构件 Component 中声明了所有用于管理成员对象的方法,包括 add,remove,getChildren 方法,确保所有的构件类都有相同的接口,在客户端看来叶子和容器都是一样的,可以透明地对待两者。它的缺点是不够安全,因为叶子和容器本质上是有区别的,叶子不可能有下一层的对象,对它调用 add,remove,getChildren 等方法是没有意义的,编译阶段没问题,如果在运行期间调用则会出错。(如果没有提供相应的错误处理代码)
示例代码:
- public abstract class Component {
- publicvoid doSomething(){
- //编写业务逻辑
- }
- publicabstract void add(Component component);
- publicabstract void remove(Component component);
- publicabstract ArrayList<Component> getChildren();
- }
- public class Composite extends Component{
- privateArrayList<Component> componentArrayList=new ArrayList<Component>();
- //增加一个叶子构件或树枝构件
- publicvoid add(Component component){
- this.componentArrayList.add(component);
- }
- //删除一个叶子构件或树枝构件
- publicvoid remove(Component component){
- this.componentArrayList.remove(component);
- }
- //获得分支下的所有叶子构件和树枝构件
- publicArrayList<Component> getChildren(){
- returnthis.componentArrayList;
- }
- }
- public class Leaf extends Component{
- @Deprecated
- publicvoid add(Component component) throws UnsupportedOperationException{
- //TODO Auto-generated method stub
- thrownew UnsupportedOperationException();
- }
- @Deprecated
- publicvoid remove(Component component)throws UnsupportedOperationException {
- //TODO Auto-generated method stub
- thrownew UnsupportedOperationException();
- }
- @Deprecated
- publicArrayList<Component> getChildren() throws UnsupportedOperationException{
- //TODO Auto-generated method stub
- thrownew UnsupportedOperationException();
- }
- }
- public class Client {
- publicstatic void display(Component root){
- for(Componentc:root.getChildren()){
- if(cinstanceof Leaf){
- c.doSomething();
- }else{
- display(c);
- }
- }
- }
- }
装饰模式
需求:我想要很方便地扩展一个类的功能或者删除功能,并且他们可以独立地变化,怎么实现呢?
特点:动态地给一个对象增加一些额外的职责。装饰模式降低了系统 的耦合度,可以动态地增加和删除对象的职责,并且使得需要装饰的具体构件类和具体装饰类可以独立变化,以便增加新的具体构件和具体装饰类。JAVA IO 流,javax、swing 包中一些图形界面构件功能都运用了装饰模式。
优点:
①对于扩展一个类的功能,装饰模式更为灵活,不会导致类的个数急剧增加。
②对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合得到功能更为强大的对象。
③符合开闭原则。
缺点:
对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。
适用场景:
①在不影响其他对象的情况下,以动态,透明地方式给单个对象添加职责。
②当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以适用装饰模式。
示例代码:
- abstractclass Component {
- public abstract void display();
- }
- publicclass ListBox extends Component {
- public void display() {
- System.out.println("显示列表框!");
- }
- }
- publicclass TextBox extends Component {
- public void display() {
- System.out.println("显示文本框!");
- }
- }
- publicclass Window extends Component {
- public void display() {
- System.out.println("显示窗体!");
- }
- }
- publicclass ComponentDecorator extends Component {
- private Component component;
- public ComponentDecorator(Component component) {
- this.component = component;
- }
- public void display() {
- component.display();
- }
- }
- publicclass ScrollBarDecorator extends ComponentDecorator {
- public ScrollBarDecorator(Component component) {
- super(component);
- }
- public void display() {
- this.setScrollBar();
- super.display();
- }
- public void setScrollBar() {
- System.out.println("为构件增加滚动条");
- }
- }
- publicclass BlackBorderDecorator extends ComponentDecorator {
- public BlackBorderDecorator(Componentcomponent) {
- super(component);
- }
- public void display() {
- this.setBlackBorder();
- super.display();
- }
- public void setBlackBorder() {
- System.out.println("为构件增加黑色边框");
- }
- }
- publicclass Client {
- public static void main(String[] args) {
- Component component,
- component2,
- component3;
- component = new Window();
- component2 = newScrollBarDecorator(component);
- //component2.display();
- component3 = newBlackBorderDecorator(component2);
- component3.display();
- }
- }
外观模式:
需求:我(客户)想要寄信,可是需要好多步骤啊,要写信,填写收件人地址和姓名,把信装入信封中,邮递信件.. 有没有一次性解决这些事情的方法?
特点:外部与一个子系统的通信通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的入口。不改变子系统对外暴露的接口,只改变内部的处理逻辑,这就是外观模式。
优点:
①减少系统间互相依赖。
②提高了安全性。不管系统内部如何变化,只要不影响到门面对象即可。并且客户只能访问门面对象上的方法。
缺点:
不遵循开闭原则,依赖门面对象,如果门面对象设计不好,只能对其进行修改,风险很大。
示例代码:
- publicinterface ILetterProcess {
- //首先要写信的内容
- public void writeContent(String context);
- //其次写封信
- public void fillEnvelope(String address);
- //把信放到信封里
- public void letterIntoEnvelope();
- //然后邮递
- public void sendLetter();
- }
- publicclass LetterProcessImpl implements ILetterProcess{
- @Override
- public void writeContent(String context) {
- // TODO Auto-generated method stub
- System.out.println("填写信的内容:"+context);
- }
- @Override
- public void fillEnvelope(String address) {
- // TODO Auto-generated method stub
- System.out.println("填写收件人地址及姓名:"+address);
- }
- @Override
- public void letterIntoEnvelope() {
- // TODO Auto-generated method stub
- System.out.println("把信放入信封中……");
- }
- @Override
- public void sendLetter() {
- // TODO Auto-generated method stub
- System.out.println("邮递信件……");
- }
- }
- publicclass ModenPostOffice {
- private ILetterProcess letterProcess=newLetterProcessImpl();
- public void sendLetter(String context,Stringaddress){
- letterProcess.writeContent(context);
- letterProcess.fillEnvelope(address);
- letterProcess.letterIntoEnvelope();
- letterProcess.sendLetter();
- }
- }
- publicclass Client {
- public static void main(String[] args){
- ModenPostOffice modenPostOffice=newModenPostOffice();
- String address="广东省SCAU";
- String context="hello world!";
- modenPostOffice.sendLetter(context, address);
- }
- }
享元模式
需求:有很多相似甚至完全一样的对象,但我不想浪费空间,每次都要 new 一个新的完全一样的对象出来。
特点:运用共享技术有效地支持大量细粒度对象的复用。在享元模式中有以下两个概念
内部状态:不会随环境而改变的状态,比如字符串 a 永远是 a。
外部状态:随环境改变,不可以共享的状态,比如字符串 a 的颜色,大小等。
优点:
极大地减少内存中对象的数量,使得相同或相似对象在内存中只保存一份从而节约资源。
缺点:
①需要分离出内部状态和外部状态,使得程序逻辑复杂。
②为了使对象共享,需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。
适用场景:
①一个系统有大量相同或者相似对象,内存大量浪费。
②对象的大部分状态都可以外部化,可以将这些外部对象传入对象中。
③需要多次重复使用享元对象。
示例代码:
- abstract class IgoChessman {
- publicabstract String getColor();
- publicvoid display(Coordinates coordinates) {
- System.out.println("棋子颜色:" + this.getColor() + ",棋子位置:" + coordinates.getX() + "," + coordinates.getY());
- }
- }
- public class BlackIgoChessman extendsIgoChessman {
- publicString getColor() {
- return "黑色";
- }
- }
- public class WhiteIgoChessman extends IgoChessman {
- publicString getColor() {
- return "白色";
- }
- }
- public class IgoChessmanFactory {
- privatestatic IgoChessmanFactory instance = new IgoChessmanFactory();
- privatestatic Hashtable ht;
- privateIgoChessmanFactory() {
- ht = newHashtable();
- IgoChessmanblack,
- white;
- black = newBlackIgoChessman();
- ht.put("b", black);
- white = newWhiteIgoChessman();
- ht.put("w", white);
- }
- publicstatic IgoChessmanFactory getInstance() {
- returninstance;
- }
- publicstatic IgoChessman getIgoChessman(String color) {
- return (IgoChessman) ht.get(color);
- }
- }
- public class Coordinates {
- privateint x,
- y;
- publicCoordinates(int x, int y) {
- this.x = x;
- this.y = y;
- }
- publicint getX() {
- returnx;
- }
- publicint getY() {
- returny;
- }
- publicvoid setX(int x) {
- this.x = x;
- }
- publicvoid setY(int y) {
- this.y = y;
- }
- }
- public class Client {
- publicstatic void main(String[] args) {
- IgoChessmanblack1,
- black2,
- black3,
- white1,
- white2;
- IgoChessmanFactoryfactory;
- //获取享元工厂对象
- factory = IgoChessmanFactory.getInstance();
- //通过享元工厂获取3颗黑子
- black1 = factory.getIgoChessman("b");
- black2 = factory.getIgoChessman("b");
- black3 = factory.getIgoChessman("b");
- System.out.println("判断两颗黑子是否相同:" + (black1 == black2));
- white1 = factory.getIgoChessman("w");
- white2 = factory.getIgoChessman("w");
- System.out.println("判断两颗白子是否相同: " + (white1 == white2));
- black1.display(newCoordinates(1, 2));
- black2.display(newCoordinates(3, 4));
- black3.display(newCoordinates(1, 3));
- white1.display(newCoordinates(2, 5));
- white2.display(new Coordinates(2, 4));
- }
- }
代理模式
特点:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的应用实在是太多了,在 JavaWeb,Spring 框架中都大量使用了代理模式。它有很多种模式,比如保护代理,远程代理,虚拟代理,动态代理,个性代理等。个性代理它在代理用户的基础上,有着它自己的功能,例如收费,代理用户之后,有个性功能的代理可以将自己的功能施加到用户身上,进行一些控制。下面的示例代码以个性代理为例,它实现了个性代理在游戏玩家升级的时候进行扣费的功能。
示例代码:
- public interface IGamePlayer {
- publicvoid login(String user,String password);
- publicvoid killBoss();
- publicvoid upgrade();
- publicIGamePlayer getProxy();
- }
- public class GamePlayer implementsIGamePlayer{
- privateString name="";
- privateIGamePlayer proxy=null;
- public GamePlayer(String name) {
- //TODO Auto-generated constructor stub
- this.name=name;
- }
- publicIGamePlayer getProxy(){
- this.proxy=newGamePlayerProxy(this);
- returnthis.proxy;
- }
- publicboolean isProxy(){
- if(this.proxy==null)return false;
- returntrue;
- }
- @Override
- publicvoid login(String user, String password) {
- //TODO Auto-generated method stub
- if(this.isProxy())
- System.out.println("登录名为"+user+"的用户"+this.name+"登录成功");
- else
- System.out.println("请使用指定的代理访问");
- }
- @Override
- publicvoid killBoss() {
- //TODO Auto-generated method stub
- if(this.isProxy())
- System.out.println(this.name+"在打怪!");
- else
- System.out.println("请使用指定的代理访问");
- }
- @Override
- publicvoid upgrade() {
- //TODO Auto-generated method stub
- if(this.isProxy())
- System.out.println(this.name+"又升了一级!");
- else
- System.out.println("请使用指定的代理访问");
- }
- }
- public interface IProxy {
- publicvoid count();
- }
- public class GamePlayerProxy implementsIGamePlayer,IProxy{
- privateIGamePlayer gamePlayer=null;
- publicGamePlayerProxy(IGamePlayer gamePlayer) {
- this.gamePlayer=gamePlayer;
- }
- @Override
- publicvoid login(String user, String password) {
- //TODO Auto-generated method stub
- this.gamePlayer.login(user,password);
- }
- @Override
- publicvoid killBoss() {
- //TODO Auto-generated method stub
- this.gamePlayer.killBoss();
- }
- @Override
- publicvoid upgrade() {
- //TODO Auto-generated method stub
- this.gamePlayer.upgrade();
- this.count();
- }
- @Override
- publicIGamePlayer getProxy() {
- //TODO Auto-generated method stub
- returnthis;
- }
- @Override
- publicvoid count() {
- //TODO Auto-generated method stub
- System.out.println("升级总费用是:150元!");
- }
- }
- public class Client {
- publicstatic void main(String[] args){
- IGamePlayerplayer=new GamePlayer("萌凯");
- IGamePlayerproxy1=player.getProxy();
- System.out.println("开始时间是:"+new Date());
- proxy1.login("MK","password");
- proxy1.killBoss();
- proxy1.upgrade();
- System.out.println("结束时间是:"+new Date());
- }
- }
行为型模式
职责链模式
需求:有很多对象可以处理我的请求,我应该将该请求发给谁呢?
特点:避免将请求发送者与接受者耦合在一起,让多个对象都有机会接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
优点:
①一个对象无须知道是其他哪一个对象处理其请求,对象仅需知道该请求会被处理即可。接受者和发送者都没有对方的明确信息,且链中的对象不需要知道链的结构,由客户端负责创建,降低系统的耦合度。
②请求处理对象仅需要维持一个指向后继者的引用,简化连接。
③在给对象分派职责时,职责链可以提供灵活性。通过在运行时对链进行动态地增加或修改或改变处理一个请求的职责。
④在系统中增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新连接链即可。
缺点:
①由于一个请求没有明确的接受者,不能保证它一定会被处理,也可能因为职责连没有被正确配置而得不到处理。
②对于比较长的职责链,请求的处理可能涉及多个对象,系统性能将受到一定影响。
③职责链连接不当可能造成死循环。
适用场景:
①有多个对象可以处理同一个请求。
②不明确指定接收者的情况下,向其中一个提交请求。
③动态指定一组对象出来请求,客户端可以动态创建责任链,还可以改变处理者的顺序。
示例代码:
- abstract class Approver {
- protectedApprover successor;//后继对象
- protectedString name;
- publicApprover(String name){
- this.name=name;
- }
- publicvoid setSuccessor(Approver successor){
- this.successor=successor;
- }
- publicabstract void processRequest(PurchaseRequest request);
- }
- public class Director extends Approver{
- publicDirector(String name){
- super(name);
- }
- publicvoid processRequest(PurchaseRequest request){
- if(request.getAmount()<50000){
- System.out.println("主任"+this.name+"审批购物单:"+request.getNumber()+
- "金额:"+request.getNumber()+"元,采购目的:"+request.getPurpose()+".");
- }else{
- this.successor.processRequest(request);//转发请求
- }
- }
- }
- public class VicePresident extendsApprover{
- publicVicePresident(String name){
- super(name);
- }
- publicvoid processRequest(PurchaseRequest request){
- if(request.getAmount()<100000){
- System.out.println("副董事长"+this.name+"审批购物单:"+request.getNumber()+
- "金额:"+request.getNumber()+"元,采购目的:"+request.getPurpose()+".");
- }else{
- this.successor.processRequest(request);//转发请求
- }
- }
- }
- public class President extends Approver{
- publicPresident(String name){
- super(name);
- }
- publicvoid processRequest(PurchaseRequest request){
- if(request.getAmount()<500000){
- System.out.println("董事长"+this.name+"审批购物单:"+request.getNumber()+
- "金额:"+request.getNumber()+"元,采购目的:"+request.getPurpose()+".");
- }else{
- this.successor.processRequest(request);//转发请求
- }
- }
- }
- public class Congress extends Approver{
- publicCongress(String name){
- super(name);
- }
- publicvoid processRequest(PurchaseRequest request){
- System.out.println("召开董事会审批购物单:"+request.getNumber()+
- "金额:"+request.getNumber()+"元,采购目的:"+request.getPurpose()+".");
- }
- }
- public class PurchaseRequest {
- privatedouble amount;
- privateint number;
- privateString purpose;
- publicPurchaseRequest(double amount,int number,String purpose){
- this.amount=amount;
- this.number=number;
- this.purpose=purpose;
- }
- publicvoid setAmount(double amount){
- this.amount=amount;
- }
- publicdouble getAmount(){
- returnamount;
- }
- publicvoid setNumber(int number){
- this.number=number;
- }
- publicint getNumber(){
- returnnumber;
- }
- publicvoid setPurpose(String purpose){
- this.purpose=purpose;
- }
- publicString getPurpose(){
- returnpurpose;
- }
- }
- public class Client {
- publicstatic void main(String[] args){
- Approverwjzhang,gyang,jguo,meeting;
- wjzhang=newDirector("wjzhang");
- gyang=newVicePresident("gyang");
- jguo=newPresident("jguo");
- meeting=newCongress("meeting");
- //创造责任链
- wjzhang.setSuccessor(gyang);
- gyang.setSuccessor(jguo);
- jguo.setSuccessor(meeting);
- PurchaseRequestpr1=new PurchaseRequest(45000, 10001, "购买倚天剑");
- wjzhang.processRequest(pr1);
- PurchaseRequestpr2=new PurchaseRequest(60000, 10001, "购买屠龙刀");
- wjzhang.processRequest(pr2);
- PurchaseRequestpr3=new PurchaseRequest(160000, 10001, "购买九阴真经");
- wjzhang.processRequest(pr3);
- PurchaseRequestpr4=new PurchaseRequest(800000, 10001, "购买桃花岛");
- wjzhang.processRequest(pr4);
- }
- }
命令模式
需求:我(客户)有时想要改需求,有时想要增加功能,有时想要删除页面,但我又不想每次都要亲自去找到对应的组别来发出命令,我需要一个跑腿的人来替我执行。
特点:将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。
优点:
①类间解耦,调用者和接受者没有任何依赖关系,调用者只需要调用 Command 抽象类的 execute 方法
②可拓展性,Command 的子类可以非常容易拓展,而调用者 Invoker 和高层次的模块 Client 不产生严重的代码耦合。
③命令模式可以结合其他模式比如责任链模式,实现命令族解析任务,或者结合模板方法模式,则可以减少 Command 子类膨胀问题。
缺点:
有 N 个命令就有 N 个子类。
适用场景:
只要认为是命令的地方就可以使用命令模式,比如模拟 DOS 系统,GUI 开发。
示例代码:
- public abstract class Group {
- //甲乙双方分开办公,如果你要和某个组讨论,需要先找到这个组
- publicabstract void find();
- //按要求增加功能
- publicabstract void add();
- //按要求删除功能
- publicabstract void delete();
- //按要求修改功能
- publicabstract void change();
- //按要求给出所有的变更计划
- publicabstract void plan();
- }
- public class PageGroup extends Group{
- @Override
- publicvoid find() {
- //TODO Auto-generated method stub
- System.out.println("找到美工组……");
- }
- @Override
- publicvoid add() {
- //TODO Auto-generated method stub
- System.out.println("客户要求增加一个页面……");
- }
- @Override
- publicvoid delete() {
- //TODO Auto-generated method stub
- System.out.println("客户要求修改一个页面……");
- }
- @Override
- publicvoid change() {
- //TODO Auto-generated method stub
- System.out.println("客户要求删除一个页面……");
- }
- @Override
- publicvoid plan() {
- //TODO Auto-generated method stub
- System.out.println("客户要求页面变更计划……");
- }
- }
- public class RequirementGroup extendsGroup{
- @Override
- publicvoid find() {
- //TODO Auto-generated method stub
- System.out.println("找到需求组……");
- }
- @Override
- publicvoid add() {
- //TODO Auto-gen
来源: http://blog.csdn.net/hemk340200600/article/details/68958675