java的几个设计模式

1.简单工厂

又叫做静态工厂方法(StaticFactory Method)模式,但不属于23种GOF设计模式之一。

简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。

spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。如下配置,就是在 HelloItxxz 类中创建一个 itxxzBean。

<beans>
  <bean id="singletonBean" class="com.itxxz.HelloItxxz">
     <constructor-arg>
       <value>Hello! 这是singletonBean!value>
     </constructor-arg>
 </ bean>
 <bean id="itxxzBean" class="com.itxxz.HelloItxxz"  singleton="false">
   <constructor-arg>
       <value>Hello! 这是itxxzBean! value>
   </constructor-arg>
 </bean>
</beans>

2.工厂模式:

简介:
工厂模式(Factory Pattern)是java中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
意图:
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

主要解决:
主要解决接口选择的问题。

何时使用:
明确地计划不同条件下创建不同实例时。

如何解决:
让其子类实现工厂接口,返回的也是一个抽象的产品。

关键代码:
创建过程在其子类执行。

应用实例:
1、你需要一辆汽车,可以直接冲工厂里面提货,而不同去管这辆汽车时怎么做出来的,以及这个汽车里面的具体实现。2、Hibernate换数据库只需要换方言和驱动就可以。

优点:
1、一个调用者想创建一个对象,只要知道其名称就可以了。2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:
每次增加一个产品时,都需要增加要给具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。

使用场景:
1、日志记录器:记录可能记录到本地硬盘,系统事件、远程服务器等,用户可以选择记录日志到什么地方。2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。3、设计一个连接服务器的框架,需要三个协议,“POP3"、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。

注意事项:
作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别时只需要通过new就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

实现:

创建一个Shape接口和实现Shape接口的实体类。下一步时定义工厂类ShapeFactory。FactoryPatternDemo类使用ShapeFactory来获取Shape对象。它将向ShapeFactory传递信息,以便获取它所需对象的类型。

代码示例:

//创建一个Shape接口
public interface Shape{
    void draw();
}
 
//实现接口的实体类
public class Rectangle implements Shape{
 
    @Override
    public void draw(){
        System.out.println("inside rectangle::draw()");
    }
}
 
public class Square implements Shape {
 
   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}
 
public class Circle implements Shape {
 
   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}
//创建一个工厂,生成基于给定信息的实体类的对象
 
public class ShapeFactory {
    
   //使用 getShape 方法获取形状类型的对象
   public Shape getShape(String shapeType){
      if(shapeType == null){
         return null;
      }        
      if(shapeType.equalsIgnoreCase("CIRCLE")){
         return new Circle();
      } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();
      } else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }
      return null;
   }
}
//使用该工厂,通过传递类型信息来获取实体类的对象
public class FactoryPatternDemo {
 
   public static void main(String[] args) {
      ShapeFactory shapeFactory = new ShapeFactory();
 
      //获取 Circle 的对象,并调用它的 draw 方法
      Shape shape1 = shapeFactory.getShape("CIRCLE");
      //调用 Circle 的 draw 方法
      shape1.draw();
 
      //获取 Rectangle 的对象,并调用它的 draw 方法
      Shape shape2 = shapeFactory.getShape("RECTANGLE");
      //调用 Rectangle 的 draw 方法
      shape2.draw();
 
      //获取 Square 的对象,并调用它的 draw 方法
      Shape shape3 = shapeFactory.getShape("SQUARE");
      //调用 Square 的 draw 方法
      shape3.draw();
   }
}

3.单例模式

单例模式是java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象方式,可以直接访问,不需要实例化该类的对象。

注意:

单例类只能有一个实例。
单例类必须自己创建自己的唯一实例。
单例类必须给所有其他对象提供这一实例。

意图:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:
一个全局使用的类频繁地创建于销毁。

何时使用:
需要控制实例数目,节省系统资源的时候。

如何解决:
判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:
构造函数是私有的。

应用实例:

一个班级只有一个班主任。
Windows是进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所有有文件的处理必须通过唯一的实例来进行。
一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
优点:
在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
避免对资源的多重占用(比如写文件操作)。

缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

使用场景:
要求生产唯一序列号。
WEB中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
创建的一个对象需要消耗的资源过多,比如I/O与数据库的连接等。
注意事项:getInstance()方法中需要使用同步锁synchronized(Singleton.class)防止多线程同时进入造成instance被多次实例化。

实现:
创建一个SingleObject类。SingleObject类有它的私有构造函数和本身一个静态实例。

SingleObject类提供了一个静态方法,供外界获取它的静态实例。SingletonPatternDemo类使用SingleObject类来获取SingleObject对象。

流程:
在这里插入图片描述
代码:

//创建一个Singleton类
//SingleObject.java
public class SingleObject{
    //创建SingleObject的一个对象
    private static SingleObject instance = new SingleObject();
 
    //让构造函数为private,这样该类就不会被实例化
    private SingleObject(){}
 
    //获取唯一可用的对象
    public static SingleObject getInstance(){
        return instance;
    }
    
    public void showMessage(){
        System.out.println("hello world");
    }
}
//从singleton类获取唯一的对象
//SingletonPatternDemo.java
public static void main(String[] args){
    //获取唯一可用的对象
    SingleObject object = SingleObject.getInstance();
    //显示消息
    object.showMessge();
}

适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。比如,读卡器是作为内存卡和笔记本之间的适配器。将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

意图:
将一个类的接口转换成客户系统的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

主要解决:
主要解决在软件系统中,常常要将一些“现存对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。

何时使用:
系统需要使用现有的类,而此类的接口不符合系统的需要。
想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定由一致的接口。
通过接口转换,将一个类插入另一个类中,比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。
如何解决:继承或依赖

关键代码
适配器继承或依赖已有的对象,实现想要的目标接口。

应用实例:
美国电器110v,中国220v,就需要一个适配器将110v转化为220v。
优点:
可以让任何两个没有关联的类一起运行。
提高了类的复用。
增加了类的透明度。
灵活性好。
缺点:
过多地使用适配器,会让系统非常零乱,不易整体进行把我。如此如果不是很有必要,可以不适用适配器,而是直接对系统进行重构。
由于JAVA至多继承一个类,所以之多只能适配一个适配者类,而且目标类必须是抽象类。
使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。

注意事项:
适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。

实现

有一个MediaPlayer接口和一个实现了MediaPlayer接口的实体类AudioPlayer。默认情况下,AudioPlayer可以播放map3格式的音频文件。还有另一个接口AdvancedMediaPlayer和实现了AdvancedMediaPlayer接口的实体类。该类可以播放vlc和mp4格式的文件。

我们想让AudioPlayer播放其他格式的音频文件。为了实现这个功能,我们需要创建一个实现了MediaPlayer接口的适配器类MediaAdapter,并使用AdvancedMediaPlayer对象来播放所需的格式。

AudioPlayer使用适配器类MediaAdapter传递所需的音频类型,不需要知道所能播放所需格式音频的实际类。AdapterPatternDemo类使用AudioPlayer类来播放各种格式。

//为媒体播放器和更高级的媒体播放器创建接口:
//MediaPlayer.java
public interface MediaPlayer{
    public void play(String audioType,String fileName);
}
//AdvancedMediaPlayer.java
public interface AdvancedMediaPlayer{
    public void playVlc(String fileName);
    public void playMap4(String fileName);
}
//创建实现了AdvancedMediaPlayer接口的实体类:
//VlcPlayer.java
public class VlcPlayer implements AdvancedMediaPlayer{
    @Override
    public void playVlc(String fileName){
        System.out.println("Playing vlc file. Name: "+ fileName); 
    }
    @Override
    public void playMp4(String fileName){
        //do nothing
    }
}
//Mp4Player.java
public class Mp4Player implements AdvancedMediaPlayer{
    @Override
    public void playVlc(String fileName){
        //do nothing
    }
    @Override
    public void playMap4(String fileName){
        System.out.println("Playing mp4 file. Name: "+ fileName);  
    }
}
//创建实现了MediaPlayer接口的适配器类
//MediaAdapter.java
public class MediaAdapter implements MediaPlayer{
    AdvancedMediaPlayer advancedMusicPlayer;
 
    public MediaAdapter(String audioType){
        if (audioType.equalsIgnoreCase("vlc")){
            advancedMusicPlayer = new VlcPlayer();
        }else if (audioType.equalsIgnoreCase("mp4")){
            advancedMusicPlayer = new Mp4Player();
        }
    }
    @Override
   public void play(String audioType, String fileName) {
      if(audioType.equalsIgnoreCase("vlc")){
         advancedMusicPlayer.playVlc(fileName);
      }else if(audioType.equalsIgnoreCase("mp4")){
         advancedMusicPlayer.playMp4(fileName);
      }
   }
 
}
//实现了MediaPlayer接口的实体类:
//AudioPlayer.java
public class AudioPlayer implements MediaPlayer{
    MediaAdapter mediaAdapter;
 
    @Override
    pblic void paly(String audioType,String fileName){
        //播放mp3音乐文件的内置支持
        if (audioType.equalsIgnoreCase("mp3")){
            System.out.println("playing mp3 file.name:"+fileName);
        }
        //mediaAdapter提供了播放其他文件格式的支持
        else if (audioType.equalsIgnoreCase("vlc")||audioType.equalsIgnoreCase("mp4")){
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType,fileName);
        }
        else{
            System.out.println("Invalid media. "+
            audioType + " format not supported");
        }
    }
}
//使用AudioPlayer来播放不同类型的音频格式:
//AdapterPatternDemo.java
public class AdapterPatternDemo{
    public static void main(String[] args){
        AudioPlayer audioPlayer = new AudioPlayer();
        audioPlayer.play("mp3", "beyond the horizon.mp3");
        audioPlayer.play("mp4", "alone.mp4");
        audioPlayer.play("vlc", "far far away.vlc");
        audioPlayer.play("avi", "mind me.avi");
    }
}

执行程序,输出结果:

Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported

装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

意图:
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

主要解决:
一般的,为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:
在不想增加很多子类的情况下扩展类。

如何解决:
将具体功能职责划分,同时继承装饰者模式。

关键代码:

Commponent类充当抽象角色,不应该具体实现。
修饰类引用和继承Commponent类,具体扩展类重写父类方法。
应用实例:
孙悟空有72变,当他变成“庙宇”后,他根本上还是一只猴子,但是又有了庙宇的功能。
无论一幅画有没有画框都可以改在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰可以动态扩展一个实现类的功能。

缺点:
多层装饰比较复杂。

使用场景:
扩展一个类的功能。
动态增加功能,动态撤销。
注意事项:可代替继承。

实现:
创建一个Shape接口和实现了Shape接口的实体类。然后创建一个实现了Shape接口的抽象装饰类ShapeDecorator,并把Shape对象作为它的实例变量。

RedShapeDecorator是实现了ShapeDecorator的实体类。DecoratorPatternDemo类使用RedShapeDecorator来装饰Shape对象。

//创建Shape接口:
//Shape.java
public interface Shape{
    void draw();
}
//创建实现Shape接口的实体类:
//Rectangle.java
public class Rectangle implements Shape{
    @Override
    public void draw(){
        System.out.println("Shape:Rectangle");
    }
}
//Circle.java
public class Circle implements Shape{
    @Override
    public void draw(){
        System.out.println("Shape: Circle");
    }
}
// 创建实现了Shape接口的抽象装饰类:
//ShapeDecorator.java
public abstract class ShapeDecorator implements Shape{
    protected Shape decoratedShape;
    public ShapeDecorator(Shape decoratedShape){
        this.decoratedShape = decoratedShape;
    }
    public void draw(){
        decoratedShape.draw();
    }
}
//创建扩展了ShapeDecorator类的实体装饰类:
//RedShapeDecorator.java
public class RedShapeDecorator extends ShapeDecorator{
    public RedShapeDecorator(Shape decoratedShape){
        super(decoratedShape);
    }
    @Override
    public void draw() {
      decoratedShape.draw();         
      setRedBorder(decoratedShape);
    }
 
    private void setRedBorder(Shape decoratedShape){
      System.out.println("Border Color: Red");
   }
}
//使用RedShapeDecorator来装饰Shape对象
//DecoratorPatternDemo.java
pulblic class DecoratorPatternDemo{
    public class void main(String[] args){
        Shape circle = new Circle();
        ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
        ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
        //Shape redCircle = new RedShapeDecorator(new Circle());
        //Shape redRectangle = new RedShapeDecorator(new Rectangle());
        System.out.println("Circle with normal border");
        circle.draw();
 
        System.out.println("\nCircle of red border");
        redCircle.draw();
 
        System.out.println("\nRectangle of red border");
        redRectangle.draw();
    }
}

执行程序,输出结果:

Circle with normal border
Shape: Circle
 
Circle of red border
Shape: Circle
Border Color: Red
 
Rectangle of red border
Shape: Rectangle
Border Color: Red

代理模式

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

意图:
为其他对象提供一种代理以控制对这个对象的访问。

主要解决:
在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些曹祖需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

何时使用:
想在访问一个类时做一些控制。

如何解决:
增加中间层。

关键代码:
实现与被代理类组合。

应用实例:

Windows里面的快捷方式。
猪八戒去找高翠兰结果时孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以所孙悟空是高翠兰代理类。
买火车票不一定在火车站买,也可以去代售点。
一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。
Sping AOP。
优点:
职责清晰。
高扩展性。
智能化。
缺点:
由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

使用场景:
远程代理。
虚拟代理。
Copy-on-Write代理。
保护代理。
Cache代理。
防火墙代理。
同步化代理。
智能引用代理。

注意事项:
和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制

实现:
创建一个Image接口和实现了Image接口的实体类。ProxyImage是一个代理类,减少RealImage对象加载的内存占用。

ProxyPatternDemo类使用ProxyImage来获取要加载的Image对象,并按照要求进行显示。

//创建Image.java接口:
//Image.java
public interface Image {
   void display();
}
//创建实现接口的实体类:
//RealImage.java
public class RealImage implements Image {
 
   private String fileName;
 
   public RealImage(String fileName){
      this.fileName = fileName;
      loadFromDisk(fileName);
   }
 
   @Override
   public void display() {
      System.out.println("Displaying " + fileName);
   }
 
   private void loadFromDisk(String fileName){
      System.out.println("Loading " + fileName);
   }
}
//当被请求时,使用PorxyImage来获取RealImage类的对象:
//ProxyPatternDemo.java
public class ProxyPatternDemo {
   
   public static void main(String[] args) {
      Image image = new ProxyImage("test_10mb.jpg");
 
      // 图像将从磁盘加载
      image.display(); 
      System.out.println("");
      // 图像不需要从磁盘加载
      image.display();  
   }
}

执行程序,输出结果:

Loadingproxy.png
Displayingproxy.png
 
Displayingproxy.png

参考

java设计模式大体上分为三大类: 创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。 结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。 行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 设计模式遵循的原则有6个: 1、开闭原则(Open Close Principle)   对扩展开放,对修改关闭。 2、里氏代换原则(Liskov Substitution Principle)   只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。 3、依赖倒转原则(Dependence Inversion Principle)   这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体。 4、接口隔离原则(Interface Segregation Principle)   使用多个隔离的借口来降低耦合度。 5、迪米特法则(最少知道原则)(Demeter Principle)   一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。 6、合成复用原则(Composite Reuse Principle)   原则是尽量使用合成/聚合的方式,而不是使用继承。继承实际上破坏了类的封装性,超类的方法可能会被子类修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木泽锐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值