رابط فلوئنت
در مهندسی نرمافزار یک رابط فصیح (به انگلیسی: Fluent interface) (که توسط اریک ایوانز و مارتین فاولر) راهی است برای پیادهسازی رابط برنامهنویسی (به انگلیسی: Application Programming Interface یا API) که کدهایی با خوانایی بهتر را پشتیبانی کند.
محتویات |
نمونهها [ویرایش]
دلفی [ویرایش]
unit FluentInterface; interface type IConfiguration = interface procedure SetColor(Color: string); procedure SetHeight(height: integer); procedure SetLength(length: integer); procedure SetDepth(depth: integer); end; IConfigurationFluent = interface function SetColor(Color: string): IConfigurationFluent; function SetHeight(height: integer): IConfigurationFluent; function SetLength(length: integer): IConfigurationFluent; function SetDepth(depth: integer): IConfigurationFluent; end; TConfiguration = class(TInterfacedObject, IConfiguration) private FColor: string; FHeight: integer; FLength: integer; FDepth: integer; protected procedure SetColor(Color: string); procedure SetHeight(height: integer); procedure SetLength(length: integer); procedure SetDepth(depth: integer); end; TConfigurationFluent = class(TInterfacedObject, IConfigurationFluent) private FColor: string; FHeight: integer; FLength: integer; FDepth: integer; protected function SetColor(Color: string): IConfigurationFluent; function SetHeight(height: integer): IConfigurationFluent; function SetLength(length: integer): IConfigurationFluent; function SetDepth(depth: integer): IConfigurationFluent; public class function New: IConfigurationFluent; end; implementation procedure TConfiguration.SetColor(Color: string); begin FColor := Color; end; procedure TConfiguration.SetDepth(depth: integer); begin FDepth := depth; end; procedure TConfiguration.SetHeight(height: integer); begin FHeight := height; end; procedure TConfiguration.SetLength(length: integer); begin FLength := length; end; class function TConfigurationFluent.New: IConfigurationFluent; begin Result := Create; end; function TConfigurationFluent.SetColor(Color: string): IConfigurationFluent; begin FColor := Color; Result := Self; end; function TConfigurationFluent.SetDepth(depth: integer): IConfigurationFluent; begin FDepth := depth; Result := Self; end; function TConfigurationFluent.SetHeight(height: integer): IConfigurationFluent; begin FHeight := height; Result := Self; end; function TConfigurationFluent.SetLength(length: integer): IConfigurationFluent; begin FLength := length; Result := Self; end; end.
Basic Usage:
var C, D: IConfiguration; E: IConfigurationFluent; begin C := TConfiguration.Create; C.SetColor('blue'); C.SetHeight(1); C.SetLength(2); C.SetDepth(3); { same "non-fluent interface" accessed using the "with" statement } D := TConfiguration.Create; with D do begin SetColor('blue'); SetHeight(1); SetLength(2); SetDepth(3) end; E := TConfigurationFluent.New .SetColor('Blue') .SetHeight(1) .SetLength(2) .SetDepth(3); end;
C [ویرایش]
namespace Example.FluentInterfaces { #region Standard Example public interface IConfiguration { string Color { set; } int Height { set; } int Length { set; } int Depth { set; } } public class Configuration : IConfiguration { string color; int height; int length; int depth; public string Color { set { color = value; } } public int Height { set { height = value; } } public int Length { set { length = value; } } public int Depth { set { depth = value; } } } #endregion #region Fluent Example public interface IConfigurationFluent { IConfigurationFluent SetColor(string color); IConfigurationFluent SetHeight(int height); IConfigurationFluent SetLength(int length); IConfigurationFluent SetDepth(int depth); } public class ConfigurationFluent : IConfigurationFluent { string color; int height; int length; int depth; public IConfigurationFluent SetColor(string color) { this.color = color; return this; } public IConfigurationFluent SetHeight(int height) { this.height = height; return this; } public IConfigurationFluent SetLength(int length) { this.length = length; return this; } public IConfigurationFluent SetDepth(int depth) { this.depth = depth; return this; } } #endregion public class ExampleProgram { public static void Main(string[] args) { //Standard Example IConfiguration config = new Configuration { Color = "blue", Height = 1, Length = 2, Depth = 3 }; //Fluent Example IConfigurationFluent fluentConfig = new ConfigurationFluent().SetColor("blue") .SetHeight(1) .SetLength(2) .SetDepth(3); } } }
C++ [ویرایش]
// basic definition class GlutApp { private: int w_, h_, x_, y_, argc_, display_mode_; char **argv_; char *title_; public: GlutApp(int argc, char** argv) { argc_ = argc; argv_ = argv; } void setDisplayMode(int mode) { display_mode_ = mode; } int getDisplayMode() { return display_mode_; } void setWindowSize(int w, int h) { w_ = w; h_ = h; } void setWindowPosition(int x, int y) { x_ = x; y_ = y; } void setTitle(const char *title) { title_ = title; } void create(); }; // basic usage int main(int argc, char **argv) { GlutApp app(argc, argv); app.setDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_ALPHA|GLUT_DEPTH); // Set framebuffer params app.setWindowSize(500, 500); // Set window params app.setWindowPosition(200, 200); app.setTitle("My OpenGL/GLUT App"); app.create(); } // Fluent wrapper class FluentGlutApp : private GlutApp { public: FluentGlutApp(int argc, char **argv) : GlutApp(argc, argv) {} // inherit parent constructor FluentGlutApp &withDoubleBuffer() { setDisplayMode(getDisplayMode() | GLUT_DOUBLE); return *this; } FluentGlutApp &withRGBA() { setDisplayMode(getDisplayMode() | GLUT_RGBA); return *this; } FluentGlutApp &withAlpha() { setDisplayMode(getDisplayMode() | GLUT_ALPHA); return *this; } FluentGlutApp &withDepth() { setDisplayMode(getDisplayMode() | GLUT_DEPTH); return *this; } FluentGlutApp &across(int w, int h) { setWindowSize(w, h); return *this; } FluentGlutApp &at(int x, int y) { setWindowPosition(x, y); return *this; } FluentGlutApp &named(const char *title) { setTitle(title); return *this; } // it doesn't make sense to chain after create(), so don't return *this void create() { GlutApp::create(); } }; // basic usage int main(int argc, char **argv) { FluentGlutApp(argc, argv) .withDoubleBuffer().withRGBA().withAlpha().withDepth() .at(200, 200).across(500, 500) .named("My OpenGL/GLUT App") .create(); }
Java [ویرایش]
public Collection<Student> findByNameAgeGender(String name, int age, Gender gender) { return em.createNamedQuery("Student.findByNameAgeGender") .setParameter("name", name) .setParameter("age", age) .setParameter("gender", gender) .setFirstResult(1) .setMaxResults(30) .setHint("hintName", "hintValue") .getResultList(); }
String[] datesStr = new String[] {"12-10-1492", "06-12-1978" }; ... List<Calendar> dates = Op.on(datesStr).toList().map(FnString.toCalendar("dd-MM-yyyy")).get();
Collection mockCollection = EasyMock.createMock(Collection.class); EasyMock.expect(mockCollection.remove(null)).andThrow(new NullPointerException()).atLeastOnce();
نمونههای دیگر جاوا [ویرایش]
In Java, the LayoutManager interface defines how Container objects can have controlled Component placement. One of the more powerful LayoutManager implementations is the GridBagLayout class which requires the use of the GridBagConstraints class to specify how layout control occurs. A typical example of the use of this class is something like the following.
GridBagLayout gl = new GridBagLayout();
JPanel p = new JPanel();
p.setLayout( gl );
JLabel l = new JLabel("Name:");
JTextField nm = new JTextField(10);
GridBagConstraints gc = new GridBagConstraints();
gc.gridx = 0;
gc.gridy = 0;
gc.fill = GridBagConstraints.NONE;
p.add( l, gc );
gc.gridx = 1;
gc.fill = GridBagConstraints.HORIZONTAL;
gc.weightx = 1;
p.add( nm, gc );
This creates a lot of code and makes it difficult to see what exactly is happening here. The Packer class, visible at http://packer.dev.java.net, provides a Fluent mechanism for using this class so that you would instead write:
JPanel p = new JPanel();
Packer pk = new Packer( p );
JLabel l = new JLabel("Name:");
JTextField nm = new JTextField(10);
pk.pack( l ).gridx(0).gridy(0);
pk.pack( nm ).gridx(1).gridy(0).fillx();
There are many places where Fluent APIs can greatly simplify how software is written and help create an API language that helps users be much more productive and comfortable with the API because the return value of a method always provides a context for further actions in that context.
PHP [ویرایش]
The following is an example of a class with a fluent interface implemented in PHP:
class Car { private $speed; private $color; private $doors; public function setSpeed($speed){ $this->speed = $speed; return $this; } public function setColor($color) { $this->color = $color; return $this; } public function setDoors($doors) { $this->doors = $doors; return $this; } } // Fluent interface $myCar = new Car(); $myCar->setSpeed(100)->setColor('blue')->setDoors(5); // Example without fluent interface $myCar2 = new Car(); $myCar2->setSpeed(100); $myCar2->setColor('blue'); $myCar2->setDoors(5);
JavaScript [ویرایش]
The following is an example of a class with a fluent interface implemented in جاوااسکریپت:
var Car = function(){ var speed, color, doors, pub; function setSpeed(new_speed) { speed = new_speed; return pub; } function setColor(new_color) { color = new_color; return pub; } function setDoors(new_doors) { doors = new_doors; return pub; } pub = { 'setSpeed': setSpeed, 'setColor': setColor, 'setDoors': setDoors, }; return pub; }; // Fluent interface myCar = Car(); myCar.setSpeed(100).setColor('blue').setDoors(5); // Example without fluent interface myCar2 = Car(); myCar2.setSpeed(100); myCar2.setColor('blue'); myCar2.setDoors(5);