常用的设计模式介绍

【Python】解决 ERROR: Exception:Traceback (most recent call last):(亲测有效) 1、遇到问题2、问题解决 阅读详情

1.    设计模式的起源

最早提出“设计模式”概念的是建筑设计大师亚力山大Alexander。在1970年他的《建筑的永恒之道》里描述了投计模式的发现,因为它已经存在了千百年之久,而现代才被通过大量的研究而被发现。

在《建筑的永恒之道》里这样描述:模式是一条由三个部分组成的通用规则:它表示了一个特定环境、一类问题和一个解决方案之间的关系。每一个模式描述了一个不断重复发生的问题,以及该问题解决方案的核心设计。

 

在他的另一本书《建筑模式语言》中提到了现在已经定义了253种模式。比如:

说明城市主要的结构:亚文化区的镶嵌、分散的工作点、城市的魅力、地方交通区

住宅团组:户型混合、公共性的程度、住宅团组、联排式住宅、丘状住宅、老人天地室内环境和室外环境、阴和阳总是一气呵成

针对住宅:夫妻的领域、儿童的领域、朝东的卧室、农家的厨房、私家的沿街露台、个人居室、起居空间的序列、多床卧室、浴室、大储藏室

针对办公室、车间和公共建筑物:灵活办公空间、共同进餐、共同小组、宾至如归、等候场所、小会议室、半私密办公室

 

尽管亚力山大的著作是针对建筑领域的,但他的观点实际上适用于所有的工程设计领域,其中也包括软件设计领域。“软件设计模式”,这个术语是在1990年代由Erich Gamma等人从建筑设计领域引入到计算机科学中来的。目前主要有23种。

 

2.    软件设计模式的分类

2.1.  创建型

创建对象时,不再由我们直接实例化对象;而是根据特定场景,由程序来确定创建对象的方式,从而保证更大的性能、更好的架构优势。创建型模式主要有简单工厂模式(并不是23种设计模式之一)、工厂方法、抽象工厂模式、单例模式、生成器模式和原型模式。

2.2.  结构型

用于帮助将多个对象组织成更大的结构。结构型模式主要有适配器模式adapter、桥接模式bridge、组合器模式component、装饰器模式decorator、门面模式、亨元模式flyweight和代理模式proxy。

2.3.  行为型

用于帮助系统间各对象的通信,以及如何控制复杂系统中流程。行为型模式主要有命令模式command、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式state、策略模式、模板模式和访问者模式。

3.    常见设计模式介绍

3.1.  单例模式(singleton)

有些时候,允许自由创建某个类的实例没有意义,还可能造成系统性能下降。如果一个类始终只能创建一个实例,则这个类被称为单例类,这种模式就被称为单例模式。

    一般建议单例模式的方法命名为:getInstance(),这个方法的返回类型肯定是单例类的类型了。getInstance方法可以有参数,这些参数可能是创建类实例所需要的参数,当然,大多数情况下是不需要的

publicclass Singleton {

   

    publicstaticvoid main(String[] args)

    {

       //创建Singleton对象不能通过构造器,只能通过getInstance方法

       Singleton s1 = Singleton.getInstance();

       Singleton s2 = Singleton.getInstance();

       //将输出true

       System.out.println(s1 == s2);

    }

   

    //使用一个变量来缓存曾经创建的实例

    privatestatic Singleton instance;

    //将构造器使用private修饰,隐藏该构造器

    private Singleton(){

       System.out.println("Singleton被构造!");

    }

   

    //提供一个静态方法,用于返回Singleton实例

    //该方法可以加入自定义的控制,保证只产生一个Singleton对象

    publicstatic Singleton getInstance()

    {

       //如果instance为null,表明还不曾创建Singleton对象

       //如果instance不为null,则表明已经创建了Singleton对象,将不会执行该方法

       if (instance == null)

       {

           //创建一个Singleton对象,并将其缓存起来

           instance = new Singleton();

       }

       returninstance;

    }

}

单例模式主要有如下两个优势:

1)      减少创建Java实例所带来的系统开销

2)      便于系统跟踪单个Java实例的生命周期、实例状态等。

 

3.2.  简单工厂(StaticFactory Method)

简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。

A实例调用B实例的方法,称为A依赖于B。如果使用new关键字来创建一个B实例(硬编码耦合),然后调用B实例的方法。一旦系统需要重构:需要使用C类来代替B类时,程序不得不改写A类代码。而用工厂模式则不需要关心B对象的实现、创建过程。

Output,接口

publicinterface Output

{

    //接口里定义的属性只能是常量

    intMAX_CACHE_LINE = 50;

    //接口里定义的只能是public的抽象实例方法

    void out();

    void getData(String msg);

}

Printer,Output的一个实现

//让Printer类实现Output

publicclass Printer implements Output

{

    private String[] printData = new String[MAX_CACHE_LINE];

    //用以记录当前需打印的作业数

    privateintdataNum = 0;

    publicvoid out()

    {

       //只要还有作业,继续打印

       while(dataNum > 0)

       {

           System.out.println("打印机打印:" + printData[0]);

           //把作业队列整体前移一位,并将剩下的作业数减1

           System.arraycopy(printData , 1, printData, 0, --dataNum);

       }

    }

    publicvoid getData(String msg)

    {

       if (dataNum >= MAX_CACHE_LINE)

       {

           System.out.println("输出队列已满,添加失败");

       }

       else

       {

           //把打印数据添加到队列里,已保存数据的数量加1。

           printData[dataNum++] = msg;

       }

    }

}

BetterPrinter,Output的一个实现

publicclass BetterPrinter implements Output

{

    private String[] printData = new String[MAX_CACHE_LINE * 2];

    //用以记录当前需打印的作业数

    privateintdataNum = 0;

    publicvoid out()

    {

       //只要还有作业,继续打印

       while(dataNum > 0)

       {

           System.out.println("高速打印机正在打印:" + printData[0]);

           //把作业队列整体前移一位,并将剩下的作业数减1

           System.arraycopy(printData , 1, printData, 0, --dataNum);

       }

    }

    publicvoid getData(String msg)

    {

       if (dataNum >= MAX_CACHE_LINE * 2)

       {

           System.out.println("输出队列已满,添加失败");

       }

       else

       {

           //把打印数据添加到队列里,已保存数据的数量加1。

           printData[dataNum++] = msg;

       }

    }

}

OutputFactory,简单工厂类

    public Output getPrinterOutput(String type) {

       if (type.equalsIgnoreCase("better")) {

           returnnew BetterPrinter();

       } else {

           returnnew Printer();

       }

    }

Computer

publicclass Computer

{

    private Output out;

 

    public Computer(Output out)

    {

       this.out = out;

    }

    //定义一个模拟获取字符串输入的方法

    publicvoid keyIn(String msg)

    {

       out.getData(msg);

    }

    //定义一个模拟打印的方法

    publicvoid print()

    {

       out.out();

    }

    publicstaticvoid main(String[] args)

    {

       //创建OutputFactory

       OutputFactory of = new OutputFactory();

       //将Output对象传入,创建Computer对象

       Computer c = new Computer(of.getPrinterOutput("normal"));

       c.keyIn("建筑永恒之道");

       c.keyIn("建筑模式语言");

       c.print();

      

 

       c = new Computer(of.getPrinterOutput("better"));

       c.keyIn("建筑永恒之道");

       c.keyIn("建筑模式语言");

       c.print();

    }

 

使用简单工厂模式的优势:让对象的调用者和对象创建过程分离,当对象调用者需要对象时,直接向工厂请求即可。从而避免了对象的调用者与对象的实现类以硬编码方式耦合,以提高系统的可维护性、可扩展性。工厂模式也有一个小小的缺陷:当产品修改时,工厂类也要做相应的修改。

3.3.  工厂方法(Factory Method)和抽象工厂(Abstract Factory)

如果我们不想在工厂类中进行逻辑判断,程序可以为不同产品类提供不同的工厂,不同的工厂类和产不同的产品。

当使用工厂方法设计模式时,对象调用者需要与具体的工厂类耦合,如:

//工厂类的定义1

publicclass BetterPrinterFactory

    implements OutputFactory

{

    public Output getOutput()

    {

       //该工厂只负责产生BetterPrinter对象

       returnnew BetterPrinter();

    }

}

//工厂类的定义2

publicclass PrinterFactory

    implements OutputFactory

{

    public Output getOutput()

    {

       //该工厂只负责产生Printer对象

       returnnew Printer();

    }

}

//工厂类的调用

//OutputFactory of = new BetterPrinterFactory();

OutputFactory of = new PrinterFactory();

Computer c = new Computer(of.getOutput());

 

使用简单工厂类,需要在工厂类里做逻辑判断。而工厂类虽然不用在工厂类做判断。但是带来了另一种耦合:客户端代码与不同的工厂类耦合。

 

为了解决客户端代码与不同工厂类耦合的问题。在工厂类的基础上再增加一个工厂类,该工厂类不制造具体的被调用对象,而是制造不同工厂对象。如:

//抽象工厂类的定义,在工厂类的基础上再建一个工厂类

publicclass OutputFactoryFactory

{

    //仅定义一个方法用于返回输出设备。

    publicstatic OutputFactory getOutputFactory(

       String type)

    {

       if (type.equalsIgnoreCase("better"))

       {

           returnnew BetterPrinterFactory();

       }

       else

       {

           returnnew PrinterFactory();

       }

    }

}

 

//抽象工厂类的调用

OutputFactory of = OutputFactoryFactory.getOutputFactory("better");

Computer c = new Computer(of.getOutput());

 

3.4.  代理模式(Proxy)

代理模式是一种应用非常广泛的设计模式,当客户端代码需要调用某个对象时,客户端实际上不关心是否准确得到该对象,它只要一个能提供该功能的对象即可,此时我们就可返回该对象的代理(Proxy)。

代理就是一个Java对象代表另一个Java对象来采取行动。如:

publicclass ImageProxy implements Image

{

    //组合一个image实例,作为被代理的对象

    private Image image;

    //使用抽象实体来初始化代理对象

    public ImageProxy(Image image)

    {

       this.image = image;

    }

    /**

     * 重写Image接口的show()方法

     * 该方法用于控制对被代理对象的访问,

     * 并根据需要负责创建和删除被代理对象

     */

    publicvoid show()

    {

       //只有当真正需要调用image的show方法时才创建被代理对象

       if (image == null)

       {

           image = new BigImage();

       }

       image.show();

    }

}

调用时,先不创建:

Image image = new ImageProxy(null);

 

Hibernate默认启用延迟加载,当系统加载A实体时,A实体关联的B实体并未被加载出来,A实体所关联的B实体全部是代理对象——只有等到A实体真正需要访问B实体时,系统才会去数据库里抓取B实体所对应的记录。

借助于Java提供的Proxy和InvocationHandler,可以实现在运行时生成动态代理的功能,而动态代理对象就可以作为目标对象使用,而且增强了目标对象的功能。如:

Panther

publicinterface Panther

{

    //info方法声明

    publicvoid info();

    //run方法声明

    publicvoid run();

}

GunPanther

publicclass GunPanther implements Panther

{

    //info方法实现,仅仅打印一个字符串

    publicvoid info()

    {

       System.out.println("我是一只猎豹!");

    }

    //run方法实现,仅仅打印一个字符串

    publicvoid run()

    {

       System.out.println("我奔跑迅速");

    }

}

MyProxyFactory,创建代理对象

publicclass MyProxyFactory

{

    //为指定target生成动态代理对象

    publicstatic Object getProxy(Object target)

       throws Exception

    {

       //创建一个MyInvokationHandler对象

       MyInvokationHandler handler =

           new MyInvokationHandler();

       //为MyInvokationHandler设置target对象

       handler.setTarget(target);

       //创建、并返回一个动态代理

       return Proxy.newProxyInstance(target.getClass().getClassLoader()

           , target.getClass().getInterfaces(), handler);

    }

}

MyInvokationHandler,增强代理的功能

publicclass MyInvokationHandler implements InvocationHandler

{

    //需要被代理的对象

    private Object target;

    publicvoid setTarget(Object target)

    {

       this.target = target;

    }

    //执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法

    public Object invoke(Object proxy, Method method, Object[] args)

       throws Exception

    {

       TxUtil tx = new TxUtil();

       //执行TxUtil对象中的beginTx。

       tx.beginTx();

       //以target作为主调来执行method方法

       Object result = method.invoke(target , args);

       //执行TxUtil对象中的endTx。

       tx.endTx();

       return result;

    }

}

TxUtil

publicclass TxUtil

{

    //第一个拦截器方法:模拟事务开始

    publicvoid beginTx()

    {

       System.out.println("=====模拟开始事务=====");

    }

    //第二个拦截器方法:模拟事务结束

    publicvoid endTx()

    {

       System.out.println("=====模拟结束事务=====");

    }

}

测试

    publicstaticvoid main(String[] args)

       throws Exception

    {

       //创建一个原始的GunDog对象,作为target

       Panther target = new GunPanther();

       //以指定的target来创建动态代理

       Panther panther = (Panther)MyProxyFactory.getProxy(target);

       //调用代理对象的info()和run()方法

       panther.info();

       panther.run();

    }

Spring所创建的AOP代理就是这种动态代理。但是Spring AOP更灵活。

3.5.  命令模式(Command)

某个方法需要完成某一个功能,完成这个功能的大部分步骤已经确定了,但可能有少量具体步骤无法确定,必须等到执行该方法时才可以确定。(在某些编程语言如Ruby、Perl里,允许传入一个代码块作为参数。但Jara暂时还不支持代码块作为参数)。在Java中,传入该方法的是一个对象,该对象通常是某个接口的匿名实现类的实例,该接口通常被称为命令接口,这种设计方式也被称为命令模式。

如:

Command

publicinterface Command

{

    //接口里定义的process方法用于封装“处理行为”

    void process(int[] target);

}

ProcessArray

publicclass ProcessArray

{

    //定义一个each()方法,用于处理数组,

    publicvoid each(int[] target , Command cmd)

    {

       cmd.process(target);

    }

}

TestCommand

publicclass TestCommand

{

    publicstaticvoid main(String[] args)

    {

       ProcessArray pa = new ProcessArray();

       int[] target = {3, -4, 6, 4};

       //第一次处理数组,具体处理行为取决于Command对象

       pa.each(target , new Command()

       {

           //重写process()方法,决定具体的处理行为

           publicvoid process(int[] target)

           {

              for (int tmp : target )

              {

                  System.out.println("迭代输出目标数组的元素:" + tmp);

              }

           }

       });

       System.out.println("------------------");

       //第二次处理数组,具体处理行为取决于Command对象

       pa.each(target , new Command()

       {

           //重写process方法,决定具体的处理行为

           publicvoid process(int[] target)

           {

              int sum = 0;

              for (int tmp : target )

              {

                  sum += tmp;         

              }

              System.out.println("数组元素的总和是:" + sum);

           }

       });

    }

}

 

HibernateTemplate使用了executeXxx()方法弥补了HibernateTemplate的不足,该方法需要接受一个HibernateCallback接口,该接口的代码如下:

public interface HibernateCallback

{

       Object doInHibernate(Session session);

}

 

3.6.  策略模式(Strategy)

策略模式用于封装系列的算法,这些算法通常被封装在一个被称为Context的类中,客户端程序可以自由选择其中一种算法,或让Context为客户端选择一种最佳算法——使用策略模式的优势是为了支持算法的自由切换。

DiscountStrategy,折扣方法接口

publicinterface DiscountStrategy

{

    //定义一个用于计算打折价的方法

    double getDiscount(double originPrice);

}

OldDiscount,旧书打折算法

publicclass OldDiscount implements DiscountStrategy {

    // 重写getDiscount()方法,提供旧书打折算法

    publicdouble getDiscount(double originPrice) {

       System.out.println("使用旧书折扣...");

       return originPrice * 0.7;

    }

}

VipDiscount,VIP打折算法

//实现DiscountStrategy接口,实现对VIP打折的算法

publicclass VipDiscount implements DiscountStrategy {

    // 重写getDiscount()方法,提供VIP打折算法

    publicdouble getDiscount(double originPrice) {

       System.out.println("使用VIP折扣...");

       return originPrice * 0.5;

    }

}

策略定义

publicclass DiscountContext

{

    //组合一个DiscountStrategy对象

    private DiscountStrategy strategy;

    //构造器,传入一个DiscountStrategy对象

    public DiscountContext(DiscountStrategy strategy)

    {

       this.strategy  = strategy;

    }

    //根据实际所使用的DiscountStrategy对象得到折扣价

    publicdouble getDiscountPrice(double price)

    {

       //如果strategy为null,系统自动选择OldDiscount类

       if (strategy == null)

       {

           strategy = new OldDiscount();

       }

       returnthis.strategy.getDiscount(price);

    }

    //提供切换算法的方法

    publicvoid setDiscount(DiscountStrategy strategy)

    {

       this.strategy = strategy;

    }

}

测试

    publicstaticvoid main(String[] args)

    {

       //客户端没有选择打折策略类

       DiscountContext dc = new DiscountContext(null);

       double price1 = 79;

       //使用默认的打折策略

       System.out.println("79元的书默认打折后的价格是:"

           + dc.getDiscountPrice(price1));

       //客户端选择合适的VIP打折策略

       dc.setDiscount(new VipDiscount());

       double price2 = 89;

       //使用VIP打折得到打折价格

       System.out.println("89元的书对VIP用户的价格是:"

           + dc.getDiscountPrice(price2));

    }

使用策略模式可以让客户端代码在不同的打折策略之间切换,但也有一个小小的遗憾:客户端代码需要和不同的策略耦合。为了弥补这个不足,我们可以考虑使用配置文件来指定DiscountContext使用哪种打折策略——这就彻底分离客户端代码和具体打折策略类。

3.7.  门面模式(Facade)

随着系统的不断改进和开发,它们会变得越来越复杂,系统会生成大量的类,这使得程序流程更难被理解。门面模式可为这些类提供一个简化的接口,从而简化访问这些类的复杂性。

       门面模式(Facade)也被称为正面模式、外观模式,这种模式用于将一组复杂的类包装到一个简单的外部接口中。

 

原来的方式

       // 依次创建三个部门实例

       Payment pay = new PaymentImpl();

       Cook cook = new CookImpl();

       Waiter waiter = new WaiterImpl();

       // 依次调用三个部门实例的方法来实现用餐功能

       String food = pay.pay();

       food = cook.cook(food);

       waiter.serve(food);

门面模式

publicclass Facade {

    // 定义被Facade封装的三个部门

    Payment pay;

    Cook cook;

    Waiter waiter;

 

    // 构造器

    public Facade() {

       this.pay = new PaymentImpl();

       this.cook = new CookImpl();

       this.waiter = new WaiterImpl();

    }

 

    publicvoid serveFood() {

       // 依次调用三个部门的方法,封装成一个serveFood()方法

       String food = pay.pay();

       food = cook.cook(food);

       waiter.serve(food);

    }

}

门面模式调用

       Facade f = new Facade();

       f.serveFood();

3.8.  桥接模式(Bridge)

由于实际的需要,某个类具有两个以上的维度变化,如果只是使用继承将无法实现这种需要,或者使得设计变得相当臃肿。而桥接模式的做法是把变化部分抽象出来,使变化部分与主类分离开来,从而将多个的变化彻底分离。最后提供一个管理类来组合不同维度上的变化,通过这种组合来满足业务的需要。

Peppery口味风格接口:

publicinterface Peppery

{

    String style();

}

口味之一

publicclass PepperySytle implements Peppery

{

    //实现"辣味"风格的方法

    public String style()

    {

       return"辣味很重,很过瘾...";

    }

}

口味之二

publicclass PlainStyle implements Peppery

{

    //实现"不辣"风格的方法

    public String style()

    {

       return"味道清淡,很养胃...";

    }

}

口味的桥梁

publicabstractclass AbstractNoodle

{

    //组合一个Peppery变量,用于将该维度的变化独立出来

    protected Peppery style;

    //每份Noodle必须组合一个Peppery对象

    public AbstractNoodle(Peppery style)

    {

       this.style = style;

    }

    publicabstractvoid eat();

}

材料之一,继承口味

publicclass PorkyNoodle extends AbstractNoodle

{

    public PorkyNoodle(Peppery style)

    {

       super(style);

    }

    //实现eat()抽象方法

    publicvoid eat()

    {

       System.out.println("这是一碗稍嫌油腻的猪肉面条。"

           + super.style.style());

    }

}

材料之二,继承口味

publicclass BeefMoodle extends AbstractNoodle

{

    public BeefMoodle(Peppery style)

    {

       super(style);

    }

    //实现eat()抽象方法

    publicvoid eat()

    {

       System.out.println("这是一碗美味的牛肉面条。"

           + super.style.style());

    }

}

主程序

publicclass Test

{

    publicstaticvoid main(String[] args)

    {

       //下面将得到“辣味”的牛肉面

       AbstractNoodle noodle1 = new BeefMoodle(

           new PepperySytle());

       noodle1.eat();

       //下面将得到“不辣”的牛肉面

       AbstractNoodle noodle2 = new BeefMoodle(

           new PlainStyle());

       noodle2.eat();

       //下面将得到“辣味”的猪肉面

       AbstractNoodle noodle3 = new PorkyNoodle(

           new PepperySytle());

       noodle3.eat();

       //下面将得到“不辣”的猪肉面

       AbstractNoodle noodle4 = new PorkyNoodle(

           new PlainStyle());

       noodle4.eat();

    }

}

Java EE应用中常见的DAO模式正是桥接模式的应用。

实际上,一个设计优良的项目,本身就是设计模式最好的教科书,例如Spring框架,当你深入阅读其源代码时,你会发现这个框架处处充满了设计模式的应用场景。

http://www.cnblogs.com/liuling/archive/2013/04/20/observer.html

3.9.  观察者模式(Observer)

 观察者模式结构中包括四种角色:

  一、主题:主题是一个接口,该接口规定了具体主题需要实现的方法,比如添加、删除观察者以及通知观察者更新数据的方法。

  二、观察者:观察者也是一个接口,该接口规定了具体观察者用来更新数据的方法。

  三、具体主题:具体主题是一个实现主题接口的类,该类包含了会经常发生变化的数据。而且还有一个集合,该集合存放的是观察者的引用。

  四:具体观察者:具体观察者是实现了观察者接口的一个类。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到具体主题的集合中,让自己成为它的观察者,或者让这个具体主题将自己从具体主题的集合中删除,使自己不在时它的观察者。

 

观察者模式定义了对象间的一对多依赖关系,让一个或多个观察者对象观察一个主题对象。当主题对象的状态发生变化时,系统能通知所有的依赖于此对象的观察者对象,从而使得观察者对象能够自动更新。

在观察者模式中,被观察的对象常常也被称为目标或主题(Subject),依赖的对象被称为观察者(Observer)。

Observer,观察者接口:

观察者:观察者也是一个接口,该接口规定了具体观察者用来更新数据的方法

publicinterface Observer {

    void update(Observable o, Object arg);

}

Observable,目标或主题:

主题:主题是一个接口,该接口规定了具体主题需要实现的方法,比如添加、删除观察者以及通知观察者更新数据的方法

import java.util.ArrayList;

import java.util.List;

import java.util.Iterator;

 

publicabstractclass Observable {

    // 用一个List来保存该对象上所有绑定的事件监听器

    List<Observer> observers = new ArrayList<Observer>();

 

    // 定义一个方法,用于从该主题上注册观察者

    publicvoid registObserver(Observer o) {

       observers.add(o);

    }

 

    // 定义一个方法,用于从该主题中删除观察者

    publicvoid removeObserver(Observer o) {

       observers.add(o);

    }

 

    // 通知该主题上注册的所有观察者

    publicvoid notifyObservers(Object value) {

       // 遍历注册到该被观察者上的所有观察者

       for (Iterator it = observers.iterator(); it.hasNext();) {

           Observer o = (Observer) it.next();

           // 显式每个观察者的update方法

           o.update(this, value);

       }

    }

}

Product被观察类:

具体主题:具体主题是一个实现主题接口的类,该类包含了会经常发生变化的数据。而且还有一个集合,该集合存放的是观察者的引用。

publicclass Product extends Observable {

    // 定义两个属性

    private String name;

    privatedoubleprice;

 

    // 无参数的构造器

    public Product() {

    }

 

    public Product(String name, double price) {

       this.name = name;

       this.price = price;

    }

 

    public String getName() {

       returnname;

    }

 

    // 当程序调用name的setter方法来修改Product的name属性时

    // 程序自然触发该对象上注册的所有观察者

    publicvoid setName(String name) {

       this.name = name;

       notifyObservers(name);

    }

 

    publicdouble getPrice() {

       returnprice;

    }

 

    // 当程序调用price的setter方法来修改Product的price属性时

    // 程序自然触发该对象上注册的所有观察者

    publicvoid setPrice(double price) {

       this.price = price;

       notifyObservers(price);

    }

}

 

具体观察者:具体观察者是实现了观察者接口的一个类。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到具体主题的集合中,让自己成为它的观察者,或者让这个具体主题将自己从具体主题的集合中删除,使自己不在时它的观察者。

 

NameObserver名称观察者:

import javax.swing.JFrame;

import javax.swing.JLabel;

 

publicclass NameObserver implements Observer {

    // 实现观察者必须实现的update方法

    publicvoid update(Observable o, Object arg) {

       if (arg instanceof String) {

           // 产品名称改变值在name中

           String name = (String) arg;

           // 启动一个JFrame窗口来显示被观察对象的状态改变

           JFrame f = new JFrame("观察者");

           JLabel l = new JLabel("名称改变为:" + name);

           f.add(l);

           f.pack();

           f.setVisible(true);

           System.out.println("名称观察者:" + o + "物品名称已经改变为: " + name);

       }

    }

}

PriceObserver价格观察者:

publicclass PriceObserver implements Observer {

    // 实现观察者必须实现的update方法

    publicvoid update(Observable o, Object arg) {

       if (arg instanceof Double) {

           System.out.println("价格观察者:" + o + "物品价格已经改变为: " + arg);

       }

    }

}

测试:

publicclass Test {

    publicstaticvoid main(String[] args) {

       // 创建一个被观察者对象

       Product p = new Product("电视机", 176);

       // 创建两个观察者对象

       NameObserver no = new NameObserver();

       PriceObserver po = new PriceObserver();

       // 向被观察对象上注册两个观察者对象

       p.registObserver(no);

       p.registObserver(po);

       // 程序调用setter方法来改变Product的name和price属性

       p.setName("书桌");

       p.setPrice(345f);

    }

}

 

其中Java工具类提供了被观察者抽象基类:java.util.Observable。观察者接口:java.util.Observer。

 

我们可以把观察者接口理解成事件监听接口,而被观察者对象也可当成事件源处理——换个角度来思考:监听,观察,这两个词语之间有本质的区别吗?Java事件机制的底层实现,本身就是通过观察者模式来实现的。除此之外,主题/订阅模式下的JMS本身就是观察者模式的应用。

LangChain从入门到精通最全教程 LangChain是一个用于构建基于大语言模型(LLM数据感知:连接外部数据源(文档、API等)智能代理:通过工具调用实现自主决策模块化设计:支持链式调用、记忆管理、提示词工程等from langchain . llms import BaseLLM class CustomLLM(BaseLLM) : def _call(self , prompt : str , ** kwargs) - > str : return f"自定义响应: { 阅读详情

相关推荐

Ubuntu开发环境配置——持续更新

本文简要介绍了Ubuntu下的开发环境配置流程和问题分析与解决,更多内容持续更新中。

m0_53365889的博客 2224

七种常用的设计模式

常用的七种设计模式:单例模式、工厂方法模式、抽象工厂模式、代理模式、装饰器模式、观察者模式和责任链模式。设计模式分类设计模式根据工作的目的,分为创建型模式、结构型模式和行为型模式三类。创建型模式:单例模式、工厂方法模式、抽象工厂模式、创建者模式、原型模式。结构型模式:适配器模式、代理模式、装饰器模式、外观模式、桥接模式、组合模式、享元模式。行为型模式:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。软件设计七大原则(OOP原则)

a154555的博客 8万+

基于cat12和SPM12进行VBM&SBM数据分析笔记1——数据预处理

基于cat12数据分析基础教程笔记 今年是小编步入研究生生活的第一年,研究方向待定,但主要以磁共振成像为主,以后会不断地总结这方面的知识,涉及MRI扫描,数据分析基础方法理论,软件操作教程,文献阅读笔记总结等等,用来记录我的研究生三年学习过程。编写尽量简洁明了,通俗易懂,本就是小白,也能体会小白的痛。不过这也要谢谢一个人手动@小白(网名),是他教我要把自己所学的写下来,讲出来才能真正的做到输入和输出,查漏补缺。而cat12软件操作是我面临的第一个数据分析的训练,写的可能也不会非常到位,还望各位专业人士指出改

Sophia2023的博客 4万+

Java 中的 23 种设计模式详解

项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

微笑听雨 7万+

热测试芯片(TTC)在ASIC热设计中的关键作用与应用

热测试芯片(TTC)是半导体热管理领域的重要工具,通过物理仿真技术精确测量芯片温度分布。其核心原理是利用金属膜电阻和二极管传感器阵列,实现微米级空间分辨率和毫秒级动态响应。在工程实践中,TTC能有效预防热岛效应、优化散热设计,特别适用于AI加速芯片、3D堆叠封装等先进半导体器件。相比传统热仿真软件,TTC提供更真实的温度数据,帮助工程师在流片前发现潜在热问题。随着1mm微型单元技术的突破,TTC已能检测0.3mm²微小区域的热变化,满足GaN/SiC等宽禁带半导体器件的测试需求。

weixin_33688840的博客 509

新代永久解锁西门子smart S7-200PLC 上传程序步骤

西门子 SMART S7-200 PLC 程序上传步骤 打开编程软件 或左下角依次打开 程序 所有程序 Siemens Automation Simatic STEP 7-MicroWIN SMART V2.4 STEP 7-MicroWIN SMART 技术交流 plcjiesuo1 2. 确认plc电源端以及输入电压; 3.用网线连接plc与电脑技术交流 plcjiesuo1 4.plc通电; 5.打开编程软件,双击通讯 6.按照...

m0_60773720的博客 2万+

Java七种常用设计模式

抽象工厂模式(Abstract Factory Pattern)是Java中常用的一种创建型设计模式,它提供了一种创建一系列相关或相互依赖的对象接口,而无需指定它们具体的类。抽象工厂模式和工厂方法模式类似,不同之处在于抽象工厂模式中工厂类不单独生产一种产品,而是生产一系列相关的产品。定义抽象产品类:void use();定义具体产品类:System . out . println("使用ProductA1");System . out . println("使用ProductA2");

qq_45196093的博客 2万+

群晖NAS硬盘挂了别慌!用Ubuntu 18外接USB保姆级数据抢救指南

本文提供群晖NAS硬盘故障时的数据抢救指南,详细介绍了使用Ubuntu 18外接USB硬盘盒的完整流程。从构建救援环境、硬盘识别到数据迁移策略,涵盖关键步骤和实用技巧,帮助用户零成本恢复重要数据。特别适合非RAID配置的单盘故障场景。

9o8p7i6u5y的博客 1146

盘点常用的九种设计模式

常用设计模式简析

txwy12的博客 2414

TC3XX - MCAL知识点(二十):CAN MCAL配置及代码实战(CAN/CANFD/extenen CAN)

CAN驱动程序负责提供AUTOSAR指定的标准CAN通信服务。M_CAN单元是底层的CAN硬件单元,它由共享消息RAM(在AUTOSAR中称为硬件对象)的节点(在AUTOSAR中称为控制器)组成。CAN驱动提供以下服务:·初始化CAN控制器以控制CAN控制器的行为和状态·设置和修改CAN控制器的波特率配置·支持CAN和CAN FD帧的收发·在轮询和中断模式下,成功的帧传输通知,专用和FIFO消息的接收和总线事件通知·数据接收使用接收FIFO功能·假装网络模式处理·支持多读/写周期功能。

梦想技术家 3939

手把手教你学基于 Linux 的 NPU 固件开发-第 10 讲:可靠性设计(RAS)——让系统坚如磐石错误检测、纠正与上报机制(ECC、Parity)

组件推荐保护机制备注HBM/DDRScrubbing必须开启,定期巡检片上 SRAMECC(权重/激活值),Parity(指令)权衡面积与可靠性计算单元ABFT或DMR(关键路径)防止计算逻辑错误片上网络CRC重传保证数据传输完整性系统软件AER 上报CE 监控自动恢复闭环管理,杜绝 SDC运维策略预测性维护Checkpoint变被动为主动核心心法(信任硬件,但必须验证。在纳米尺度下,错误是必然发生的物理现象。

MHD0815的博客 91

【设计模式】常用的10种设计模式

收录一些自己在开发过程中比较常用的模式,整理出来以便自己梳理和复习,从而熟能生巧,举一反三。下面只列出模式的脉络大纲,取最核心的逻辑进行讲解。

Kard的博客 2274

LabVIEW实现HTTP通信

本节简要介绍HTTP通信的基础知识,并以工业物联网平台TLINK中云端设备的数据发布设计为例,阐述互联网环境下HTTP客户端应用程序设计的基本方法。重点讲解工业物联网平台TLINK中HTTP设备的创建、通信数据帧的构造与测试。

不脱发的程序猿 1545

常用设计模式介绍

工厂方法设计模式(Factory Method Pattern)是一种创建型设计模式,它提供了一种封装对象创建的方式。通过这种模式,父类将对象的创建延迟到子类中实现,从而使得基类或接口的代码与具体实现解耦。工厂方法设计模式的核心思想:定义一个用于创建对象的接口,但让实现这个接口的类决定实例化哪一个类。工厂方法让类的实例化推迟到子类,这样客户端代码只需要依赖于抽象接口,而无需关注具体的实现细节。

2401_89221704的博客 854

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

23种常用设计模式介绍

本文介绍了Java设计模式的三大类别:创建型模式(如单例模式、原型模式、工厂模式、抽象工厂模式、建造者模式)、结构型模式(如适配器模式、组合模式、装饰器模式、外观模式、享元模式、代理模式、桥接模式)和行为型模式(如责任链模式、命令模式、解释器模式、迭代器模式、观察者模式、状态模式、模板模式、中介者模式、备忘录模式、访问者模式、策略模式)。设计模式是解决常见软件设计问题的可复用解决方案,它们提高了代码的可维护性和可扩展性。

一名80后IT人的成长记录,记录自己在工作、学习中的点点滴滴,在成长的喜悦与忧心的故事里,铭记反脆弱的勇气。 1528

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

几种常用的设计模式介绍

几种常用的设计模式介绍 1.    设计模式的起源 最早提出“设计模式”概念的是建筑设计大师亚力山大Alexander。在1970年他的《建筑的永恒之道》里描述了投计模式的发现,因为它已经存在了千百年之久,而现代才被通过大量的研究而被发现。 在《建筑的永恒之道》里这样描述:模式是一条由三个部分组成的通用规则:它表示了一个特定环境、一类问题和一个解决方案之间的关系。每一个模式描述了一个不断

sengeiou的专栏 16万+

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Java中常用的设计模式介绍

Java中常用的设计模式介绍 1.    设计模式的起源 最早提出“设计模式”概念的是建筑设计大师亚力山大Alexander。在1970年他的《建筑的永恒之道》里描述了投计模式的发现,因为它已经存在了千百年之久,而现代才被通过大量的研究而被发现。 在《建筑的永恒之道》里这样描述:模式是一条由三个部分组成的通用规则:它表示了一个特定环境、一类问题和一个解决方案之间的关系。每一个模式描述

HelenLi的博客 828

嵌入式开发常用的设计模式介绍

嵌入式开发设计模式介绍

欢迎来到TechCxm技术博客! 1388
上一篇: 备份MySQL数据库
下一篇: Redis keys 性能问题
大树叶
博客等级 码龄12年 1171粉丝 664原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值