Design Mode 1-- Strategy Mode | adapter mode | factory mode

Recently looking at 《Think In JAVA》, which explain in succession, when a combination of abstract classes and interfaces, referred to the problem of several design patterns. These design patterns also does make me a better understanding of the meaning of the data structure in JAVA. Today there are books on the combination of their own understanding, a little tidy up these design patterns.

Strategy Pattern | Strategy Pattern

Here it is necessary to mention 向上转化this concept. Inheritance and interface have mentioned this concept.
Conversion means upward succession subclass can upconvert parent class. For example, there is a class Instrument, and one of its subclasses Flute. Play subclass rewrite overrides the parent class.

    class Instrument{
        public void play(){
            ...
            //play instrument
        }
    }
    
    class Flute extends Instrument{
        public void play(){
            ...
            //play flute
        }
    }

If then there is a method needs to receive and play a musical instrument parameters , you do not need to write more overloaded methods receive a variety of different instruments, only one method Instrument class reception.

    public void play(Instrument instrument){
        instrument.play();
    }

Passed a Flute objects in this method, the call will be to play flute in the method. That's up conversion, one of the most simple example of dynamic binding.
In fact, the interface is the same reason, but the interface allows a variety of transformed upward. In other words, JAVA is the only inheritance, but the interface is Implement more. Thus JAVA inheritance path conversion only upwardly, upwardly and the interface conversion path is not unique.

 

The next step is talked about policy mode.

The concept of strategy mode as follows:

Defines a set of encapsulated algorithms that can be swapped to carry out a specific behaviour
defines a packaged set of algorithms that perform different operations respectively. In actual operation, these algorithms may dynamically switch to meet the needs of different scenarios

Strategy pattern usage scenarios are:

  1. Save the file to a different format

  2. To achieve a variety of sorting algorithms

  3. To achieve a variety of file compression

That is, a set of codes to the policy model in different ways to do the same job into different classes, respectively, and with each other in operation by switching strategy pattern.

 

 

This is the UML diagram on the strategy pattern from the Internet to find.


JAVA strategy pattern is inherited, a comprehensive application of abstract classes and interfaces.

In strategy mode, we can according to an "open interface" design a variety of "specific strategies" , then only need to enter "Open Interface" when you call , it will be decided according to the specific implementation "open interface" of the specific program is running operation result.

The above design pattern code is as follows:

//接口
public interface Strategy {
   public int doOperation(int num1, int num2);
}

//接口实现类
public class OperationAdd implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 + num2;
   }
}
public class OperationSubstract implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 - num2;
   }
}
public class OperationMultiply implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 * num2;
   }
}

//上下文
public class Context {
   private Strategy strategy;

   public Context(Strategy strategy){
      this.strategy = strategy;
   }

   public int executeStrategy(int num1, int num2){
      return strategy.doOperation(num1, num2);
   }
}

//具体调用
public class StrategyPatternDemo {
   public static void main(String[] args) {
      Context context = new Context(new OperationAdd());        
      System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

      context = new Context(new OperationSubstract());        
      System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

      context = new Context(new OperationMultiply());        
      System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
   }
}

Use the Strategy pattern and does not use the strategy pattern gap

Let me repeat that a specific example to illustrate the use of the Strategy pattern and the gap does not use the strategy pattern.

Suppose we have a file compression, compressed files have a variety of algorithms, such as Zip, RAR and so on. Selecting a compression program can perform the compression operation according to the algorithm of the operating system and actual performance parameters. We assume this function to select specific algorithm placed CompressionPreference class.


In fact, for these clients it is transparent. In other words, the client only know that there will be a compression function that requires customers to upload files to compress. Under such a scenario, the server only needs to provide a compression interface, without having to expose the implementation.

code show as below:

//选择压缩方法类,根据具体情况返回压缩的方法
public class CompressionPreference{
     public static CompressionStrategy getPreferedStrategy(){
         //根据系统的情况或是用户的选择返回具体的压缩算法
     }
}

//压缩策略接口
public interface CompressionStrategy{
    void compress(List<File> files);
}

//压缩策略的具体实现
public class ZipCompressionStrategy implements CompressionStrategy{
    @Override
    public void compress(List<File> files){
        //zip 压缩
    }
}

public class RarCompressionStrategy implements CompressionStrategy{
    @Override
    public void compress(List<File> files){
        //RAR 压缩
    }
}

public class CompressionContext{
    private CompressionStrategy strategy;
    
    //这里根据CompressionPreference选择压缩策略
    public void setStrategy(CompressionStrategy strategy){this.strategy=strategy);}
    
    public void createArchieve(List<File> files){
        strategy.compress(files);
    }
}

//客户端调用
public class Client{
    public static void main(String[] args) {
    CompressionContext ctx = new CompressionContext();
    //设置压缩上下文
    ctx.setCompressionStrategy(CompressionPreference.getPreferedStrategy());
    ctx.createArchive(fileList);
  }
}

By following this design, if you need to add a new algorithm, only we need to add a CompressionStrategy implementation class, as well as modify the method to CompressionPreference. It will not have any effect on the client's call.

Without algorithm package, allows direct client calls to say.

On the one hand, exposed to all sorts achieve compression algorithm,

On the other hand, also increased the wrong call may cause.

And once new compression algorithm increases, the client needs to know this does not need to know these things, to adjust their call. This code maintainability is really bad.

Adapter pattern | Adapter Design Pattern

definition:

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that could not otherwise because. Of incompatible interfaces
of the two incompatible converted into each other via an interface adapter.

Adapter mode: you want to use is in an existing class, but he does not meet the requirements of the interface, as in the encoding process to be followed open for extension, closed for modification of the principles, it can not modify the original class, which You will need to use an adapter mode, the original form of the class adapted to their needs. There are two kinds of object adapter class adapter and adapter.

Adapter mode than the mode better understand some of the strategies. When the book explain adapter mode, in fact, to explain how to add programming to an interface. Adapter mode, by definition, would not have been inherited by the class of an interface adapter that can be converted by the interface call, it acts as a bridge between the two even incompatible interfaces .

From the original meaning of the terms, refers to an interface adapter converter, in life, the most common interface converter is your cell phone charging thread it! Charging head from the standard 220V socket output voltage (internal) can be converted to the safe charging voltage. And a USB charging port provided at the other side, so that the phone can be charged at a charge line containing all USB ports. As another example, which is the SD card. Use the camera's friends know that some computers do not provide an SD card interface, you need an SD card into the SD card reader, then the reader into your computer via the USB interface. Then the computer can read the contents of the SD card.

From the example, the scene adapter is not applied to the modified class inherits an interface can be implemented as a class is called the interface .

Book adapter model implemented in two ways, one is through a proxy, the other is through inheritance . It is essentially the same in two ways, if need all to achieve the original class, the adapter achieved through inheritance , if only part of the implementation, by the way agents . Specific analysis of specific situations.


More than talking about are too abstract, following about a specific example.

For example, I have a scan class Scanner, he has a method, you can receive all inherited Readable interface class, and according to the class of case in which the data is read out. The system already has some categories, they could be read Scanner, but they did not inherit Readable interface.

Spirit 开闭原则, I can add an adapter to these classes, so that it can be read Scanner. In this mode, both the emergence of new Scanner reads the file or read the existing file system, do not have to modify Scanner method. Readable only need to support the interface on the line.

    public class Scanner{
        public void read(Readable material){
            material.read();
        }
    }
    
    public interface Readable{
        void read();
    }
    public class TXT implements Readable{
        ...
        public void read(){
            //读取txt文件
        }
        ...
    }
    
    public class HTML{
        public void toReadableFormat(){
            //html文件也可以被读取,但是它并没有继承Readable接口,所以无法被Scanner
            识别
        }
    }
    
    //这里才是适配器模式
    public class HTMLAdapter implements Readable{
        ...
        private HTML html;
        public HTMLAdapter(HTML html){this.html = html}
        public void read(){
            html.toReadableFormat();
        }
        ...
    }
    
    //这时候两个文件都可以被读取了
    public class Test{
        public static void main(String[] args){
            Scanner s = new Scanner();
            s.read(new TXT());
            s.read(new HTMLAdapter(new HTML()));
        }
    }

One example is not enough, come one ~

In the history of the development from the media, the media format more diverse, from the initial text to MP3, then video formats. If we now have a system that originally only supports reading files in MP3 format, this time in order that the system can support playback files of the new media category.

The new media-file developed by another team, have their own development interface and implementation. How to integrate the modules into existing systems it?

This time we need to solve this problem through an adapter mode.

 

 

This behavior is due to the UML class FIG. By creating a new MediaAdapter adapter in the original system inherits the original media player interface, so that the original system can not know the basis of the underlying changes, continue to call the original play method to achieve playback.
Specific code as follows:

    public interface MediaPlayer {
       public void play(String audioType, String fileName);
    }
    
    public interface AdvancedMediaPlayer {    
       public void playVlc(String fileName);
       public void playMp4(String fileName);
    }
    
    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
       }
    }
    
    public class Mp4Player implements AdvancedMediaPlayer{

       @Override
       public void playVlc(String fileName) {
          //do nothing
       }

       @Override
       public void playMp4(String fileName) {
          System.out.println("Playing mp4 file. Name: "+ fileName);        
       }
    }
    
    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);
          }
       }
    }
    
    public class AdapterPatternDemo {
       public static void main(String[] args) {
          MediaPlayer audioPlayer = new AudioPlayer();
          audioPlayer.play("mp3", "beyond the horizon.mp3");
          MediaPlayer videoPlayer = new MediaAdapter();
          videoPlayer.play("vlc", "far far away.vlc");
   }
}

Factory Pattern | Factory Design Pattern

And finally to a factory pattern to write for a long time ~ ~ ~ ~ ah QAQ

工厂模式是为了管理一个接口之下众多实现类。比如最常见的DAO接口。数据库中的表少至10个多可以至百个。在spring框架中,通过IOC和DI实现了这么多读取数据库的接口实现类的管理。那么在没有框架的场景下,如何才可以使上层代码和下层具体的DAO接口解耦呢?这时就需要工厂模式。通过工厂模式获得具体DAO接口

至于为什么要选择这样的一个工厂模式,而不是直接new一个具体的实现类呢?这里举个例子。比方说,有一个DAO接口,实现该接口的有UserDaoImpl, AccountDaoImpl等。假设有两个类均用到UserDaoImpl。如果在这两个类中均使用new来创建一个新的UserDaoImpl,那么一旦有一天,因为需求变更,需要将UserDaoImpl换成AnotherUserDaoImpl,则需要在两个类中分别修改。那么如果有十个类,甚至一百个类都用到了这个Dao呢?这时候如果我是通过工厂来获得这个Dao,也就只需要在工厂中将返回值从原来的UserDaoImpl变成AnotherUserDaoImpl,并不会影响调用方。


简单工厂模式 | Static Factory Method

下面给一个简单的工厂模式的例子。

interface Dog
{
  public void speak ();
}

class Poodle implements Dog
{
  public void speak()
  {
    System.out.println("The poodle says \"arf\"");
  }
}

class Rottweiler implements Dog
{
  public void speak()
  {
    System.out.println("The Rottweiler says (in a very deep voice) \"WOOF!\"");
  }
}

class SiberianHusky implements Dog
{
  public void speak()
  {
    System.out.println("The husky says \"Dude, what's up?\"");
  }
}

class DogFactory
{
  public static Dog getDog(String criteria)
  {
    if ( criteria.equals("small") )
      return new Poodle();
    else if ( criteria.equals("big") )
      return new Rottweiler();
    else if ( criteria.equals("working") )
      return new SiberianHusky();

    return null;
  }
}

public class JavaFactoryPatternExample
{
  public static void main(String[] args)
  {
    // create a small dog
    Dog dog = DogFactory.getDog("small");
    dog.speak();

    // create a big dog
    dog = DogFactory.getDog("big");
    dog.speak();

    // create a working dog
    dog = DogFactory.getDog("working");
    dog.speak();
  }
}

在简单的工厂模式中,工厂根据输入的条件返回给一个接口的具体实现。
简单工厂模式有一个问题,就是一旦工厂出现新的产品,就必须修改工厂中获取产品的方法,这有违开闭原则。

而且工厂模式承担的压力过重,可能会导致职责的混乱。最重要的是,简单工厂模式中,获取产品的方法是静态方法,该方法无法通过继承等形式得到扩展。


工厂方法模式 | Factory Method Pattern

这其实是工厂模式的一个简单的升级。考虑一个真实工厂的场景。它的产品Product之下往往还有许多分类,如轴承,轮胎。各个子分类往往也对应着不同的车间,如轴承车间,轮胎车间。

如果还用简单工厂模式返回一个Product,且不说向上转型可能丢失的一些数据,而且工厂的压力也太大了,因为可能要根据不同场景返回上百个不同类型但继承了同一接口的类。这不符合设计原则。

这时候就出现了工厂方法模式。不仅仅对产品抽象,还对工厂抽象。对不同的产品提供不同的工厂,将职责进一步细化,满足SRP(单一职责原则)。同时,因为不需要输入无关的判断数据,也解除了控制耦合。

具体例子有最常见的日志系统。日志系统之下往往针对各个不同的子系统,比如数据库日志子系统,比如文件日志子系统。不同的日志系统对应的日志文件也不同。

这时通过工厂方法模式就可以很好的解决二者之间的关系。

 

在这张图中还可以继续延伸,比如数据库包括Mysql数据库,Oracle数据库等等。在UML图中也可以继续根据MySqlLog创建MysqlLogFactory。

还有一个具体的例子就是JAVA中的数据库连接。

Connection conn=DriverManager.getConnection("jdbc:microsoft:sqlserver://loc
alhost:1433; DatabaseName=DB;user=sa;password=");

Statement statement=conn.createStatement();

ResultSet rs=statement.executeQuery("select * from UserInfo");

这里通过DriverManager工厂根据输入的信息返回一个对应的连接。

连接中再返回对应的抽象语句statement。

根据工厂中的信息可以知道,这个statement的底层实现必定是一个类似SqlServerStatement的实现。

抽象工厂模式 | Abstract Factory Method

产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。


产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。

抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。

在这里的上下文中,抽象工厂之下的子工厂被划分为海尔子工厂,海信子工厂。在海尔自工厂中可以获得海尔冰箱(productA),海尔电视机(productB), 同理,在海信子工厂中,可以获得海信冰箱(productA),海信电视机(productB)。

当然了 在大多数的应用场景下,工厂设计模式已经足够了。

 

对比1:适配器模式和抽象工厂模式的区别

工厂模式是负责加工的,适配器模式包括工厂模式,比工厂模式又高了一点点,增加了统一的抽象接口定义,方便以后在不同的数据库切换而不用改底层代码

对比2:适配器模式与装饰器模式的区别

适配器与装饰器模式的别名都是包装模式(Wrapper)。

区别

---适配器模式的意义

将一个接口转变成另一个接口,目的是通过改变接口来达到重复使用的目的。

----装饰器模式的意义

不改变被装饰对象的接口,而是保持原有的接口,增强原有对象的功能,或改变原有对象的处理方式而增提高性能。

 

Guess you like

Origin blog.csdn.net/fly910905/article/details/93459389