PUT or POST: The REST API

The crux of the issue comes down to a concept known as idempotency. An operation is idempotent if a sequence of two or more of the same operation results in the same resource state as would a single instance of that operation. According to the HTTP 1.1 specification, GET, HEAD, PUT and DELETE are idempotent, while POST is not. That is, a sequence of multiple attempts to PUT data to a URL will result in the same resource state as a single attempt to PUT data to that URL, but the same cannot be said of a POST request. This is why a browser always pops up a warning dialog when you back up over a POSTed form. “Are you sure you want to purchase that item again!?” (Would that the warning was always this clear!)

After that discussion, a more realistic mapping would seem to be:

  • Create = PUT iff you are sending the full content of the specified resource (URL).
  • Create = POST if you are sending a command to the server to create a subordinate of the specified resource, using some server-side algorithm.
  • Retrieve = GET.
  • Update = PUT iff you are updating the full content of the specified resource.
  • Update = POST if you are requesting the server to update one or more subordinates of the specified resource.
  • Delete = DELETE.

NOTE: “iff” means “if and only if”.

Analysis

Create can be implemented using an HTTP PUT, if (and only if) the payload of the request contains the full content of the exactly specified URL. For instance, assume a client issues the following Create OR Update request:

   HTTP/1.1 PUT /GrafPak/Pictures/1000.jpg
   ...

   <full content of 1000.jpg ... >

This command is idempotent because sending the same command once or five times in a row will have exactly the same effect; namely that the payload of the request will end up becoming the full content of the resource specified by the URL, “/GrafPak/Pictures/1000.jpg”.

On the other hand, the following request is NOT idempotent because the results of sending it either once or several times are different:

   HTTP/1.1 POST /GrafPak/Pictures
   ...

   <?xml version="1.0" encoding="UTF-8"?> 
   <GrafPak operation="add" type="jpeg">
     <[CDATA[ <full content of some picture ... > ]]>
   </GrafPak>

Specifically, sending this command twice will result in two “new” pictures being added to the Pictures container on the server. According to the HTTP 1.1 specification, the server’s response should be something like “201 Created” with Location headers for each response containing the resource (URL) references to the newly created resources–something like “/GrafPak/Pictures/1001.jpg” and “/GrafPak/Pictures/1002.jpg”.

The value of the Location response header allows the client application to directly address these new picture objects on the server in subsequent operations. In fact, the client application could even use PUT to directly update these new pictures in an idempotent fashion.

What it comes down to is that PUT must create or update a specified resource by sending the full content of that same resource. POST operations, on the other hand, tell a web service exactly how to modify the contents of a resource that may be considered a container of other resources. POST operations may or may not result in additional directly accessible resources.

FROM HERE

An Awesome video about Rest api, which talks about idempotent from 21minute

Advertisements

mvc vs mvp

n MVC, the model stores the data, the view is a representation of that data, and the controller allows the user to change the data. When the data is changed, all views are notified of the change and they can update themselves as necessary (think EventDispatcher).

MVP is a derivative of MVC, mostly aimed at addressing the “Application Model” portion of MVC and focusing around the observer implementation in the MVC triad. Instead of a Controller, we now have a Presenter, but the basic idea remains the same – the model stores the data, the view is a representation of that data (not necessarily graphical), and the presenter coordinates the application

 

 This search application is extremely basic and used for purely illustrative purposes only.

It consists of a single Use Case scenario; Search, which executes as follows:

  1. 1.       User enters a search criteria (e.g. “test”)
  2. 2.       The User initiates a search in the UI (e.g. Button click)
  3. 3.       The View component handles the search event, and invokes the ‘Search()’ function on the Presenter
  4. 4.       The Presenter extracts the search criteria string from the View’s ‘SearchCriteria’ property
  5. 5.       The Presenter invokes the Business operation ‘Business Search’ on the Model, passing the search criteria as an argument
  6. 6.       The Model returns all matching results for the given search criteria
  7. 7.       The Presenter sets these results in the View component’s ‘Results’ property

Java 中的Iterator模式

下午要quiz Iterator,赶紧复习了下~

通过collection 类 的 Iterator接口,可以隐藏内部实现,
貌似1.5之后都有实现了Iterable接口,可以直接foreach了,不过了解一下这种设计模式还是很有意义的。

public interface Iterator { 
 boolean hasNext(); 
 Object next(); 
 void remove(); 
} 

client一般只操作next和hasNext方法,不关心representation,达到分离的目的!

具体实现是:拿AbstracyList为例,里面先提供一个内部类:

private class Itr implements Iterator { 
… 

 而iterator()方法的定义是: 

public Iterator iterator() { 
 return new Itr(); 


因此client不知道它通过Iterator it = a.iterator();所获得的Iterator的真正类型。

现在我们关心的是这个申明为private的Itr类是如何实现遍历AbstractList的: 

private class Itr implements Iterator { 
 int cursor = 0; 
 int lastRet = -1; 
 int expectedModCount = modCount; 

  Itr类依靠3个int变量(还有一个隐含的AbstractList的引用)来实现遍历,cursor是下一次next()调用时元素的位置,第一次调用next()将返回索引为0的元素。lastRet记录上一次游标所在位置,因此它总是比cursor少1。 

  变量cursor和集合的元素个数决定hasNext(): 

public boolean hasNext() { 
 return cursor != size(); 

  方法next()返回的是索引为cursor的元素,然后修改cursor和lastRet的值: 

public Object next() { 
 checkForComodification(); 
 try { 
  Object next = get(cursor); 
  lastRet = cursor++; 
  return next; 
 } catch(IndexOutOfBoundsException e) { 
  checkForComodification(); 
  throw new NoSuchElementException(); 
 } 

 这个是亮点(判断concurrent): 

  expectedModCount表示期待的modCount值,用来判断在遍历过程中集合是否被修改过。AbstractList包含一个modCount变量,它的初始值是0,当集合每被修改一次时(调用add,remove等方法),modCount加1。因此,modCount如果不变,表示集合内容未被修改。 

  Itr初始化时用expectedModCount记录集合的modCount变量,此后在必要的地方它会检测modCount的值: 

final void checkForComodification() { 
 if (modCount != expectedModCount) 
  throw new ConcurrentModificationException(); 

  如果modCount与一开始记录在expectedModeCount中的值不等,说明集合内容被修改过,此时会抛出ConcurrentModificationException。 

  这个ConcurrentModificationException是RuntimeException,不要在客户端捕获它。如果发生此异常,说明程序代码的编写有问题,应该仔细检查代码而不是在catch中忽略它。 

  但是调用Iterator自身的remove()方法删除当前元素是完全没有问题的,因为在这个方法中会自动同步expectedModCount和modCount的值: 

public void remove() { 
… 
AbstractList.this.remove(lastRet); 
… 
// 在调用了集合的remove()方法之后重新设置了expectedModCount: 
expectedModCount = modCount; 
… 

  要确保遍历过程顺利完成,必须保证遍历过程中不更改集合的内容(Iterator的remove()方法除外),因此,确保遍历可靠的原则是只在一个线程中使用这个集合,或者在多线程中对遍历代码进行同步。 

Decorator模式(装饰者)

定义:动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活。
   设计初衷:通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的。
   实际上Java 的I/O API就是使用Decorator实现的。

   在此 以一家人作画为例写了5个java类来描述说明Decorator设计模式的实现方式;
   这个例子举的不太好,但足以说明问题。下面是具体的代码。
  

 1、 Work.java     接口
 2、 Son.java      被装饰者-负责画画
 3、 Mother.java   装饰者-负责上颜色
 4、 Father.java   装饰者-负责装画框
 5、 DecoratorTest.java 带有main方法的测试类,

        模拟3种调用情况:A,只画画;

                         B,画画,上色;

                         C, 画画,上色,装框

 

===============   1、 Work.java
package decorator;

public interface Work {
  public void paint();
}
===============   1   end

 

===============   2、 Son.java
package decorator;

public class Son implements Work {
  public void paint() {
    System.out.println(“儿子用铅笔画好了一幅画。”);
  }
}
===============   2   end

 

===============   3、 Mother.java
package decorator;

public class Mother implements Work {

  //被装饰者
  private Work work;
 
  public Mother(Work work) {
    this.work = work;
  }
 
  private Mother() {}

  public void paint() {
   
    //妈妈装饰者做的职责
    System.out.println(“妈妈正在做给画上颜色前的准备工作。”);
   
    //被装饰者做的职责
    work.paint();
   
    //妈妈装饰者做的职责
    System.out.println(“妈妈给画上好了颜色。”);
  }

}
===============   3   end

 

===============   4、 Father.java
package decorator;

public class Father implements Work {

  //被装饰者
  private Work work;
 
  public Father(Work work) {
    this.work = work;
  }
 
  private Father() {}

  public void paint() {
   
    //爸爸装饰者做的职责
    System.out.println(“爸爸正在做上画框前的准备工作。”);
   
    //被装饰者做的职责
    work.paint();
   
    //爸爸装饰者做的职责
    System.out.println(“爸爸给画装好了一个画框。”);
  }

}
===============   4   end

 

===============   5、 DecoratorTest.java
package decorator;

public class DecoratorTest {

  public static void main(String[] args) {
   
    //只画铅笔画
    Work work = new Son();
    work.paint();   
    System.out.println(“\n”);
   
    //除了画铅笔画,还要给画上颜色
    work = new Mother(work);
    work.paint();
    System.out.println(“\n”);
   
    //除了画铅笔画,给画上颜色,还要给画上画框
    work = new Father(work);
    work.paint();
   
  }
}
===============   5   end

 

输出:
儿子用铅笔画好了一幅画。

妈妈正在做给画上颜色前的准备工作。
儿子用铅笔画好了一幅画。
妈妈给画上好了颜色。

爸爸正在做上画框前的准备工作。
妈妈正在做给画上颜色前的准备工作。
儿子用铅笔画好了一幅画。
妈妈给画上好了颜色。
爸爸给画装好了一个画框。

prototype模式

要理解原型原型模式必须先理解Java里的浅复制和深复制。有的地方,复制也叫做克隆。Java提供这两种克隆方式。

    浅克隆:被克隆对象的所有变量都含有与原来的对象相同的值,而它所有的对其他对象的引用都仍然指向原来的对象。换一种说法就是浅克隆仅仅克隆所考虑的对象,而不克隆它所引用的对象。

    深克隆:被克隆对象的所有变量都含有与原来的对象相同的值,但它所有的对其他对象的引用不再是原有的,而这是指向被复制过的新对象。换言之,深复制把要复制的对象的所有引用的对象都复制了一遍,这种叫做间接复制。深复制的源代码如下:   

Java代码 复制代码

  1. public Object deepClone() throws IOException, OptionalDataException, ClassNotFoundException   
  2.   
  3.      {   
  4.   
  5.          //write to stream   
  6.   
  7.          ByteArrayOutputStream bo = new ByteArrayOutputStream();   
  8.   
  9.          ObjectOutputStream oo = new ObjectOutputStream(bo);   
  10.   
  11.          oo.writeObject(this);        //read from stream      ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());   
  12.   
  13.          ObjectInputStream oi = new ObjectInputStream(bi);   
  14.   
  15.            return (oi.readObject());   
  16.   
  17.      }  

    Java语言的构建模型直接支持原型模型模式,所有要克隆的类必须实现一个标识接口Cloneable。所有的类都有一个Clone()方法(Object提供)。克隆满足的条件:对于任何对象x,都有x.clone()!=x,换言之,克隆的对象不是原来的对象;x.clone().getClass()==x.getClass(),换言之,克隆的对象与原对象是同一类型。x.clone().equals(x)成立。

Java代码 复制代码

  1. public class Sheep implements Cloneable   
  2.   
  3.     {   
  4.   
  5.         private String name = “Dolly”;   
  6.   
  7.         public Object clone() throws CloneNotSupportedException   
  8.   
  9.         {   
  10.   
  11.       return super.clone();   
  12.   
  13.         }   
  14.   
  15.     }  

原型模式定义: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.

    Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。

如何使用? 因为Java中的提供clone()方法来实现对象的克隆,所以Prototype模式实现一下子变得很简单.

以勺子为例:

Java代码 复制代码

  1. public abstract class AbstractSpoon implements Cloneable   
  2. {   
  3.  String spoonName;   
  4.   
  5.  public void setSpoonName(String spoonName) {this.spoonName = spoonName;}   
  6.  public String getSpoonName() {return this.spoonName;}   
  7.   
  8.  public Object clone()   
  9.  {   
  10.  Object object = null;   
  11.  try {   
  12.  object = super.clone();   
  13.  } catch (CloneNotSupportedException exception) {   
  14.  System.err.println(“AbstractSpoon is not Cloneable”);   
  15.  }   
  16.  return object;   
  17.  }   
  18. }   
  19.  有个具体实现(ConcretePrototype):   
  20.   
  21. public class SoupSpoon extends AbstractSpoon   
  22. {   
  23.  public SoupSpoon()   
  24.  {   
  25.  setSpoonName(“Soup Spoon”);   
  26.  }   
  27. }  

调用Prototype模式很简单:

AbstractSpoon spoon = new SoupSpoon();
AbstractSpoon spoon2 = spoon.clone();
当然也可以结合工厂模式来创建AbstractSpoon实例。

在Java中Prototype模式变成clone()方法的使用,由于Java的纯洁的面向对象特性,使得在Java中使用设计模式变得很自然,两者已经几乎是浑然一体了。这反映在很多模式上,如Interator遍历模式。

Builder模式

定义一个文件生成器。文件的类:MyFile 分为三个部分,也就是说有三个属性:FileHead,FileContent,FileEnd。
FileHead定义如下:

package create.builder1;
public class FileHead {
 private String value;

 public String getValue() {
  return value;
 }

 public void setValue(String value) {
  this.value = value;
 }
}

FileContent定义如下:

package create.builder1;
public class FileContent {
 private String value;

 public String getValue() {
  return value;
 }

 public void setValue(String value) {
  this.value = value;
 }
}

FileEnd 定义如下:

package create.builder1;
public class FileEnd {
 private String value;

 public String getValue() {
  return value;
 }

 public void setValue(String value) {
  this.value = value;
 }
}

现在定义MyFile:
package create.builder1;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class MyFile {
 private FileHead fhead;

 private FileContent fcontent;

 private FileEnd fend;
  
 public FileContent getFcontent() {
  return fcontent;
 }

 public void setFcontent(FileContent fcontent) {
  this.fcontent = fcontent;
 }

 public FileEnd getFend() {
  return fend;
 }

 public void setFend(FileEnd fend) {
  this.fend = fend;
 }

 public FileHead getFhead() {
  return fhead;
 }

 public void setFhead(FileHead fhead) {
  this.fhead = fhead;
 }
 public void write(String path) throws IOException{
  File f=new File(path);
  FileWriter fw=new FileWriter(f);
  PrintWriter out=new PrintWriter(fw);
  out.println(fhead.getValue());
  out.println(fcontent.getValue());
  out.println(fend.getValue());
  out.close();  
 }
}

定义Builder,我给她取名字为FileBuilder:
package create.builder1;

public interface FileBuilder {
  void buildHead();
  void buildContent();
  void buildEnd();
  MyFile getResult();
}
该定义Director了,名字为:FileDirector:
package create.builder1;

public class FileDirector {
    private FileBuilder filebuilder;
    public FileDirector(FileBuilder filebuilder){
     this.filebuilder=filebuilder;
    }
    public void construct(){
     filebuilder.buildHead();
     filebuilder.buildContent();
     filebuilder.buildEnd();
    }
}

现在到了定义具体Builder的时候了,定义两个,一个是生成txt文件的,一个生成xml文件
TxtBuilder:
package create.builder1;

public class TxtBuilder implements FileBuilder{
 private FileHead fh=new FileHead();
 private FileContent fc=new FileContent();;
 private FileEnd fe=new FileEnd();

 public void buildHead() {
  // TODO Auto-generated method stub
  fh.setValue(“This is text file”);
 }

 public void buildContent() {
  // TODO Auto-generated method stub
  fc.setValue(“this is my content”);
 }

 public void buildEnd() {
  // TODO Auto-generated method stub
  fe.setValue(“this is the end of the file”);
 }

 public MyFile getResult() {
  // TODO Auto-generated method stub
  MyFile my=new MyFile();
  my.setFcontent(fc);
  my.setFhead(fh);
  my.setFend(fe);
  return my;
 }

}

XmlBuilder

package create.builder1;

public class XmlBuilder implements FileBuilder {
    private FileHead fh=new FileHead();
    private FileContent fc=new FileContent();
    private FileEnd fe=new FileEnd();
   
 public void buildHead() {
  // TODO Auto-generated method stub
  fh.setValue(“<?xml version=\”1.0\” encoding=\”GB2312\”?><content>”); 
 }

 public void buildContent() {
  // TODO Auto-generated method stub
  fc.setValue(“<test>asdasd</test>”);
 }

 public void buildEnd() {
  // TODO Auto-generated method stub
       fe.setValue(“</content>”);  
 }

 public MyFile getResult() {
  // TODO Auto-generated method stub
  MyFile my=new MyFile();
  my.setFcontent(fc);
  my.setFhead(fh);
  my.setFend(fe);
  return my;
 }

}
Demo如下:
package create.builder1;

import java.io.IOException;

public class Demo {

 public static void main(String[] args) throws IOException {
  // TODO Auto-generated method stub
        FileBuilder fb=new TxtBuilder();
        FileDirector fd=new FileDirector(fb);
       
        fd.construct();
        MyFile my=fb.getResult();
        my.write(“D:/test.txt”);
       
        FileBuilder fbxml=new XmlBuilder();
        FileDirector fdxml=new FileDirector(fbxml);
        fdxml.construct();
    
   MyFile myxml=fbxml.getResult();
        myxml.write(“D:/test1.xml”);
 }

}
在Demo里可以看到Builder模式的特点。

Command模式示例代码

Command模式示例代码
一个简单的例子,它通过Command模式实现回调机制。以Fan(风扇)和Light(灯)为例。我们的目标是设计一个Switch,它可以对任一个对象进行 ‘开’ 和 ‘关’ 的操作。Fan和Light具有不同的接口,这意味着Switch必须独立于接收者接口,或者说,它不清楚接收者的接口。为了解决这一问题,每个Switch都需要合适的command作为参数。很明显,连接到Light的Switch和连接到Fan的Switch具有不同的command。因此,Command类必须是抽象的,或者是个接口。Switch的构造函数被调用时,它以一组合适的command作为参数。command作为Switch的私有变量保存下来。调用flipUp()和flipDown()操作时,它们只是简单地让合适的command进行execute()操作。Switch对调用execute()后将发生些什么一无所知。TestCommand.java
class Fan {
        public void startRotate() {
                System.out.println(‘Fan is rotating’);
        }
        public void stopRotate() {
                System.out.println(‘Fan is not rotating’);
        }
}
class Light {
        public void turnOn( ) {
                System.out.println(‘Light is on ‘);
        }
        public void turnOff( ) {
                System.out.println(‘Light is off’);
        }
}
class Switch {
        private Command UpCommand, DownCommand;
        public Switch( Command Up, Command Down) {
                UpCommand = Up; // concrete Command registers itself with the invoker
                DownCommand = Down;
        }
        void flipUp( ) { // invoker calls back concrete Command, which executes the Command on the receiver
                        UpCommand . execute ( ) ;
        }
        void flipDown( ) {
                        DownCommand . execute ( );
        }
}
class LightOnCommand implements Command {
        private Light myLight;
        public LightOnCommand ( Light L) {
                myLight  =  L;
        }
        public void execute( ) {
                myLight . turnOn( );
        }
}
class LightOffCommand implements Command {
        private Light myLight;
        public LightOffCommand ( Light L) {
                myLight  =  L;
        }
        public void execute( ) {
                myLight . turnOff( );
        }
}
class FanOnCommand implements Command {
        private Fan myFan;
        public FanOnCommand ( Fan F) {
                myFan  =  F;
        }
        public void execute( ) {
                myFan . startRotate( );
        }
}
class FanOffCommand implements Command {
        private Fan myFan;        public FanOffCommand ( Fan F) {
                myFan  =  F;
        }
        public void execute( ) {
                myFan . stopRotate( );
        }
}
public class TestCommand {
                public static void main(String[] args) {
                        Light  testLight = new Light( );
                        LightOnCommand testLOC = new LightOnCommand(testLight);
                        LightOffCommand testLFC = new LightOffCommand(testLight);
                        Switch testSwitch = new Switch(testLOC,testLFC);      
                        testSwitch.flipUp( );
                        testSwitch.flipDown( );
                        Fan testFan = new Fan( );
                        FanOnCommand foc = new FanOnCommand(testFan);
                        FanOffCommand ffc = new FanOffCommand(testFan);
                        Switch ts = new Switch( foc,ffc);
                        ts.flipUp( );
                        ts.flipDown( );
                }
}               Command.java
public interface Command {
        public abstract void execute ( );
}在上面的示例代码中,Command模式将 ‘调用操作的对象’ (Switch)和 ‘知道如何执行操作的对象’ (Light和Fan)完全分离开来。这带来了很大的灵活性:发送请求的对象只需要知道如何发送;它不必知道如何完成请求。