OOPS Concept in PHP in HINDI | Method Chaining | how to chain methods a...

seen from United States

seen from United States
seen from United States
seen from China

seen from Norway

seen from United States

seen from Canada
seen from United States
seen from United States

seen from Netherlands
seen from China
seen from Tajikistan

seen from United States
seen from United States
seen from United States

seen from United States
seen from Tรผrkiye
seen from Tรผrkiye
seen from United States
seen from United States
OOPS Concept in PHP in HINDI | Method Chaining | how to chain methods a...
jQuery Method Chaining
jQuery Method Chaining
In jQuery Method Chaining, there is a technique called chaining, that allows us to run multiple jQuery commands, one after the other, on the same element(s). This way, browsers do not have to find the same element(s) more than once.
To chain an action, you simply append the action to the previous action. The following example chains together the css(), slideUp(), andโฆ
View On WordPress
Fluent interface in python
Fluent Interface is an implementation of API which improves readability.
Example
Poem('The Road Not Taken').indent(4).suffix('Robert Frost').
Fluent Interface is similar to method chaining. I was wondering how to implement this in Python. Returning self during method call seemed good idea .
class Poem(object): def __init__(self, content): self.content = content def indent(self, spaces=4): self.content = " " * spaces + self.content return self def suffix(self, content): self.content += " - {}".format(content) return self def __str__(self): return self.content >>>print Poem('Road Not Taken').indent(4).suffix('Rober Frost').content Road Not Taken - Rober Frost
Everything seems to be ok here.
Side effects
Above approach has side effect. We are mutating the same object during every method call. Consider the following code
>>>p = Poem('Road Not Taken') >>>q = p.indent(4) >>>r = p.indent(2) >>>print str(q) == str(r) True >>>print id(q), id(r) 4459640464 4459640464
Clearly this isn't expected. q and r is pointing to same instance. Ideally we should create a new instance during every method call and return the object.
New object
Decorator is a function which takes a function as argument and returns a function (most cases).
from functools import wraps def newobj(method): @wraps(method) # Well, newobj can be decorated with function, but we will cover the case # where it decorated with method def inner(self, *args, **kwargs): obj = self.__class__.__new__(self.__class__) obj.__dict__ = self.__dict__.copy() method(obj, *args, **kwargs) return obj return inner class NPoem(object): def __init__(self, content): self.content = content @newobj def indent(self, spaces=4): self.content = " " * spaces + self.content @newobj def suffix(self, content): self.content += " - {}".format(content) def __str__(self): return self.content >>>p = NPoem("foo") >>>q = p.indent(4) >>>r = p.indent(2) >>>print str(q) == str(r) False >>>print(q) foo >>>print(r) foo >>>print id(q), id(r) 4459642640 4459639248
In the above approach newobj decorator creates a new instance, copies all the atrributes, calls the method with arguments and returns new instance. Rather than using decorator private function can do the same.
Fluent Interface is used heavily in SQLAlchemy and Django. Django uses _clone method to create new QuerySet and SQLAlchemy uses decorator based approach to create new Query instance.
Thanks Mike Bayer and Daniel Roy Greenfeld helping me understand this.
Do you know any other way to implement this ? Feel free to comment.
๋ฐ๋ฉํ ๋ฅด์ ๋ฒ์น Law of Demeter
๋ฐ๋ฉํ ๋ฅด๋ ๊ทธ๋ฆฌ์ค ์ ํ์ ๋์ค๋ ์ถ์์ ์ ์ด๋ค. ๋ก๋ง์ ํ์์๋ ์ธ๋ ์ค Ceres ๋ผ๊ณ ๋ถ๋ฆฌ๋ ๋ฐ๋ก ๊ทธ ์ . ํ์ง๋ง, ๊ทธ ๋ฐ๋ฉํ ๋ฅดํ๊ณ ์ด ๋ฒ์นํ๊ณ ๋ ์๊ด์๋ค๋ ๊ฒ์ด ํจ์ . [์ํคํผ๋์]1์์๋ ๋ฐ๋ฉํ ๋ฅด์ ๋ฒ์น์ ์๋์ ๊ฐ์ด ์ ์ํ๊ณ ์๋ค.
๋ฐ๋ฉํ ๋ฅด์ ๋ฒ์น์์๋ ์ด๋ค ๊ฐ์ฒด O์ ๋ฉ์๋ m๋ ๋ค์๊ณผ ๊ฐ์ ์ข ๋ฅ์ ๊ฐ์ฒด์ ์๋ ๋ฉ์๋๋ค๋ง ์คํ์ํฌ ์ ์๋ค.
O ์์ฒด
m ์ ๋ณ์
m ์์์ ๋ง๋ค์ด์ง ๊ฐ์ฒด
O๊ฐ ์ง์ ๊ด๋ฆฌํ๋ ์ฝคํฌ๋ํธ ๊ฐ์ฒด
m์ ์ค์ฝํ ์์์ O๊ฐ ์ ๊ทผ ๊ฐ๋ฅํ ์ ์ญ๋ณ์
์ข ๋ง์ด ์ด๋ ค์ด๋ฐ, Richard Carr์ [The Law of Demeter]2 ํฌ์คํธ์ ์ข ๋ ์ฌ์ด ์ค๋ช ์ด ์๋ค.
์ด๋ค ํด๋ผ์ค์ ๋ฉค๋ฒ โ ๋ฉ์๋ ๋๋ ์์ฑ โ ๋ ๋ฐ๋์ ๋ค์๊ณผ ๊ฐ์ ๊ฐ์ฒด๋ค์ ๋ฉค๋ฒ๋ค๋ง์ ์คํ์์ผ์ผ ํ๋ค:
ํด๋น ๋ฉ์๋ ๋๋ ์์ฑ์ด ์ ์ธ๋ ๊ฐ์ฒด
๋ฉ์๋์ ํ๋ผ๋ฏธํฐ๋ก ๋ณด๋ด์ง ๊ฐ์ฒด
๋ฉ์๋ ๋๋ ์์ฑ์ด ์ง์ ์ด๊ธฐํ์ํจ ๊ฐ์ฒด
ํธ์ถ์ ์ํ ๋ฉ์๋ ๋๋ ์์ฑ์ผ๋ก์ ๊ฐ์ ํด๋ผ์ค ์์์ ์ ์ธ๋ ๊ฐ์ฒด
์ ์ญ ๊ฐ์ฒด
์๋ ์์ ์ฝ๋๋ฅผ ๋ณด์. ASP.NET MVC ์น์ฌ์ดํธ๋ฅผ ๊ฐ๋ฐํ๋ค๋ณด๋ฉด ์ฝํธ๋กค๋ฌ์์ ํํ ๋ณผ ์ ์๋ ์ํฉ์ด๋ค.
public class ProductController : Controller { private IProductService _service; public ProductController(IProductService service) { this._service = service; } public ActionResult Index() { var products = this._service.Repository.Get(); return View(products); } } public class ProductService : IProductService { public ProductService(IProductRepository repository) { this.Repository = repository; } public IProductRepository Repository { get; private set; } }
์์ ์ฝ๋์์ Index ์ก์ ์ ๋ณด๋ฉด ๋๋ต ์์์ด ๊ฐ๋ฅํ๊ฒ ์ง๋ง ProductService๋ผ๋ ์๋น์ค ๋ ์ด์ด ์์์ ProductRepository๋ผ๋ ๋ฐ์ดํฐ ๋ฆฌํฌ์งํ ๋ฆฌ ํจํด์ ํตํด CRUD๋ฅผ ๊ตฌํํ๊ณ ์๋ค. Index ์ก์ ์ ์ ์ฒด ์ ํ ๋ฆฌ์คํธ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๋ทฐ๋ฅผ ๊ฐ๊ณ ์์ด์ ์ ์ฒด ์ ํ ๋ฆฌ์คํธ๋ ์๋น์ค ์์ ๊ตฌํ๋ ๋ฆฌํฌ์งํ ๋ฆฌ์ Get ๋ฉ์๋๋ฅผ ํตํด ๊ฐ์ ธ์ค๊ฒ ๋๋ค. ์ด๋ ๊ฒ ๋ฉ์๋ ์ฒด์ด๋์ ํ๋ ๊ฒ์ด ๋ฐ๋ก ๋ฐ๋ฉํ ๋ฅด์ ๋ฒ์น์ ์๋ฐํ๋ ๊ฒ์ด ๋๋ค. ProductController ๊ฐ์ฒด๋ ์์ฑ์๋ฅผ ํตํด ๋ณ์๋ก ๋ฐ์ ProductService ๊ฐ์ฒด์ ๋ฉ์๋ ๋๋ ์์ฑ์ ํธ์ถํด์ผ ํ์ง ๊ทธ ๋ด๋ถ์ ์๋ ProductRepository ๊ฐ์ฒด์ Get ๋ฉ์๋๋ฅผ ์ง์ ํธ์ถํด์๋ ์๋๋ค. ProductRepository ๊ฐ์ฒด์ ํ์ฌ ์ํ๊ฐ null์ด๋ผ๋ฉด ํด๋น ์ฝ๋๋ NullReferenceException์ ๋์ง๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ผ์ ProductService ํด๋ผ์ค ์์ ์ถ๊ฐ์ ์ธ ๋ฉ์๋๋ฅผ ์ ์ธํด์ฃผ๋ ๋ฐฉ์์ผ๋ก ๋ฆฌํฉํ ๋ง์ ํด์ผ ํ๋ค.
public class ProductController : Controller { private IProductService _service; public ProductController(IProductService service) { this._service = service; } public ActionResult Index() { var products = this._service.GetProducts(); return View(products); } } public class ProductService : IProductService { private IProductRepository _repository; public ProductService(IProductRepository repository) { this._repository = repository; } public IList<Product> GetProducts() { return this._repository.Get(); } }
์ฆ ProductRepository ๊ฐ์ฒด๋ฅผ public ์์ฑ์ด๋ ํ๋๋ก ๋๋ ๊ฒ์ด ์๋๋ผ ๋ด๋ถ์ ์ผ๋ก encapsulation ์ํค๊ณ ProductRepository ํด๋ผ์ค์ ๋ฉค๋ฒ๋ ProductService ํด๋ผ์ค์ ๋ฉค๋ฒ๋ฅผ ํตํด ํธ์ถํ๋ ๋ฐฉ์์ผ๋ก ํ๊ฒ ๋๋ฉด, ProductController ํด๋ผ์ค๋ ์ง์ ์ ์ผ๋ก ๊ด๋ จ์ด ์๋ ์ฝคํฌ๋ํธ์ธ ProductSerivce์ ๋ํด์๋ง ํต์ ๊ถ์ ๊ฐ์ง ์ ์์ด์ ๋ณด๋ค ์์ ํ๊ณ ์ ์ฐํ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๊ฒ ๋๋ค.
์ด๋ฐ์์ผ๋ก ๋ฉ์๋ ์ฒด์ด๋์ ์ต๋ํ ์ค์ด๋ ๊ฒ์ด ๋ฐ๋์งํ ๊ฐ์ฒด์งํฅ ํ๋ก๊ทธ๋๋ฐ์ด๋ผ๊ณ ํ ์ ์๊ฒ ๋ค. ํ์ง๋ง ์ด๋ ๊ฒ ํ๋ก๊ทธ๋๋ฐ์ ํ๊ฒ ๋๋ฉด ์ถ๊ฐ์ ์ธ ๋ฉ์๋๋ฅผ ์์ฑํด์ผ ํ๋ ๋ถ๋ด์ด ์๊ธฐ๊ฒ ๋๋๋ฐ, ์ด๊ฒ์ด ๊ผญ ๋ถ์ ์ ์ด๋ผ๊ณ ๋ ํ ์ ์๋ ๊ฒ์ด ๊ฐ์ฒด ์ฌ์ด์ ์์กด์ฑ์ ์ต์ํํ๋ ๋ฐฉ์์ผ๋ก ์ ์ฐํ๊ฒ ๊ฐ๋ฐ์ ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋จ, LINQ๋ฅผ ์ฐ๋ ์ํฉ์ด๋ผ๋ฉด ์๊ธฐ๊ฐ ๋ฌ๋ผ์ง๋ค. LINQ์์๋ ํน์ฑ์ ๋ฉ์๋ ์ฒด์ด๋์ด ํ์์ผ ์ ๋ฐ์ ์๋์ง๋ผ, ์ด ๋ฐ๋ฉํ ๋ฅด์ ๋ฒ์น์์ ๋ฒ์ด๋ ์ ์๋๋ฐ, ๊ทธ ์ด์ ๋ ๋ฉ์๋ ์ฒด์ด๋์ ํ๋ ๊ฒ๊ณผ ์๊ด์์ด ํญ์ ๋ฆฌํดํ์ ์ด ๋์ผํ๊ธฐ ๋๋ฌธ์ด๋ค. ์์ ์์ ์ฝ๋์ ๋์จ ProductRepository ํด๋ผ์ค์ Get ๋ฉ์๋๋ ์๋ง๋ ๋ด๋ถ์ ์ผ๋ก ์๋์ ๊ฐ์ด ๊ตฌํ์ด ๋์ด ์์ ๊ฒ์ด๋ค.
public class ProductRepository : IProductRepository { private CompanyDataContext _context; public ProductRepository(ICompanyDataContext context) { this._context = context as CompanyDataContext; } public IList<Product> Get() { return this._context .Products .Where(p => p.IsActive) .OrderBy(p => p.DateRegistered) .ToList(); } }
์ฌ๊ธฐ์ Products, Where ๊ทธ๋ฆฌ๊ณ OrderBy๋ ๋ชจ๋ ๋์ผํ IEnumerable<Product> ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค. ์ฆ LINQ๋ฅผ ์ด์ฉํ ๋ฉ์๋ ์ฒด์ด๋์ ๊ฒฝ์ฐ ๋ฉ์๋๋ง๋ค ๋์ผํ ๋ฐ์ดํฐ ํ์ ์ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ์ด ๋ฐ๋ฉํ ๋ฅด์ ๋ฒ์น์ ์๋ฐํ์ง ์๊ณ ์์ ํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
์ฐธ์กฐ:
[Law of Demeter]1 from Wikipedia
[The Law of Demeter]2 by Richard Carr
http://en.wikipedia.org/wiki/Law_of_Demeterย โฉ๏ธ โฉ๏ธ
http://www.blackwasp.co.uk/LawOfDemeter.aspxย โฉ๏ธ โฉ๏ธ
Eric Feminella explains how to use the method chaining pattern to both simplify your API and make it more fluent.