Design patterns
ビルダーパターン
サーチ…
備考
複雑なオブジェクトの構成をその表現から切り離し、同じ構築プロセスで異なる表現を作成できるようにします。
- ロジックと表現を分けてください。
- ロジックを再利用して、異なるデータセットを処理します。
Builderパターン/ C#/ Fluent Interrface
public class Email
{
public string To { get; set; }
public string From { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}
public class EmailBuilder
{
private readonly Email _email;
public EmailBuilder()
{
_email = new Email();
}
public EmailBuilder To(string address)
{
_email.To = address;
return this;
}
public EmailBuilder From(string address)
{
_email.From = address;
return this;
}
public EmailBuilder Subject(string title)
{
_email.Subject = title;
return this;
}
public EmailBuilder Body(string content)
{
_email.Body = content;
return this;
}
public Email Build()
{
return _email;
}
}
使用例:
var emailBuilder = new EmailBuilder();
var email = emailBuilder
.To("[email protected]")
.From("[email protected]")
.Subject("Email subject")
.Body("Email content")
.Build();
ビルダーパターン/ Java実装
Builderパターンを使用すると、多くのオプション変数を含むクラスのインスタンスを読みやすい方法で作成できます。
次のコードを考えてみましょう:
public class Computer {
public GraphicsCard graphicsCard;
public Monitor[] monitors;
public Processor processor;
public Memory[] ram;
//more class variables here...
Computer(GraphicsCard g, Monitor[] m, Processer p, Memory ram) {
//code omitted for brevity...
}
//class methods omitted...
}
すべてのパラメータが必要な場合は、これはすべてうまくいっています。多くの変数があり、そのうちのいくつかがオプションの場合はどうなりますか?必要なパラメータとオプションのパラメータを組み合わせて多数のコンストラクタを作成したくない場合は、メンテナンスが難しくなり、開発者が理解できるようになります。また、多くのユーザーがnullとして入力する必要があるパラメータの長いリストを持つことは望ましくない場合があります。
Builderパターンは、Builderと呼ばれる内部クラスを作成します。これは、必要なオプション変数のみをインスタンス化するために使用されます。これは、変数型をパラメータとし、Builderオブジェクトを返すオプションの各変数のメソッドを使用して実行され、メソッドを相互にチェーンすることができます。ビルドコンストラクタに必要な変数があれば、それらを省略することはできません。
Builderにはbuild()
というメソッドも含まれています。このメソッドは、オブジェクトの構築時にメソッド呼び出しのチェーンの終わりに呼び出されなければならないオブジェクトを返します。
前の例に続いて、このコードは、コンピュータクラスのBuilderパターンを使用します。
public class Computer {
private GraphicsCard graphicsCard;
private Monitor[] monitors;
private Processor processor;
private Memory[] ram;
//more class variables here...
private Computer(Builder builder) {
this.graphicsCard = builder.graphicsCard;
this.monitors = builder.monitors;
this.processor = builder.processor;
this.ram = builder.ram;
}
public GraphicsCard getGraphicsCard() {
return this.graphicsCard;
}
public Monitor[] getMonitors() {
return this.monitors;
}
public Processor getProcessor() {
return this.processor;
}
public Memory[] getRam() {
return this.ram;
}
public static class Builder {
private GraphicsCard graphicsCard;
private Monitor[] monitors;
private Processor processor;
private Memory[] ram;
public Builder(Processor p){
this.processor = p;
}
public Builder graphicsCard(GraphicsCard g) {
this.graphicsCard = g;
return this;
}
public Builder monitors(Monitor[] mg) {
this.monitors = mg;
return this;
}
public Builder ram(Memory[] ram) {
this.ram = ram;
return this;
}
public Computer build() {
return new Computer(this);
}
}
}
このクラスの使用例
public class ComputerExample {
public static void main(String[] args) {
Computer headlessComputer = new Computer.Builder(new Processor("Intel-i3"))
.graphicsCard(new GraphicsCard("GTX-960"))
.build();
Computer gamingPC = new Computer.Builder(new Processor("Intel-i7-quadcode"))
.graphicsCard(new GraphicsCard("DX11"))
.monitors(new Monitor[] = {new Monitor("acer-s7"), new Monitor("acer-s7")})
.ram(new Memory[] = {new Memory("2GB"), new Memory("2GB"), new Memory("2GB"), new Memory("2GB")})
.build();
}
}
この例は、ビルダー・パターンが、かなり簡単にクラスを作成する方法に多くの柔軟性を与える方法を示しています。コンピュータオブジェクトは、呼び出し側が希望する構成に基づいて、容易に読みやすい方法で実装できます。
合成によるJavaのビルダーパターン
意図:
複雑なオブジェクトの構成をその表現から分離して、同じ構築プロセスで異なる表現を作成できるようにする
Builderパターンは、必須の属性がほとんどなく、オブジェクトを構築するための多くのオプションの属性がある場合に便利です。さまざまな必須属性とオプションの属性を持つオブジェクトを作成するには、オブジェクトを作成するための複雑なコンストラクタを用意する必要があります。ビルダーパターンは、複雑なオブジェクトを作成するための簡単なステップバイステップのプロセスを提供します。
実生活ユースケース:
FaceBookの異なるユーザーは、ユーザー名のような必須属性と、UserBasicInfoやContactInfoなどのオプションの属性で構成される、異なる属性を持っています。一部のユーザーは単純に基本情報を提供します。一部のユーザーは連絡先情報などの詳細情報を提供しています。 Builderパターンがない場合は、すべての必須パラメータとオプションパラメータをコンストラクタに提供する必要があります。しかしBuilderパターンは、複雑なオブジェクトを構築するための簡単なステップバイステップのプロセスを提供することにより、構築プロセスを単純化します。
ヒント:
- 静的なネストされたBuilderクラスを提供します。
- オブジェクトの必須属性のコンストラクタを提供します。
- オブジェクトのオプション属性のセッターメソッドとゲッターメソッドを提供します。
- オプションの属性を設定した後、同じBuilderオブジェクトを返します。
- 複雑なオブジェクトを返すbuild()メソッドを提供する
コードスニペット:
import java.util.*;
class UserBasicInfo{
String nickName;
String birthDate;
String gender;
public UserBasicInfo(String name,String date,String gender){
this.nickName = name;
this.birthDate = date;
this.gender = gender;
}
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("Name:DOB:Gender:").append(nickName).append(":").append(birthDate).append(":").
append(gender);
return sb.toString();
}
}
class ContactInfo{
String eMail;
String mobileHome;
String mobileWork;
public ContactInfo(String mail, String homeNo, String mobileOff){
this.eMail = mail;
this.mobileHome = homeNo;
this.mobileWork = mobileOff;
}
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("email:mobile(H):mobile(W):").append(eMail).append(":").append(mobileHome).append(":").append(mobileWork);
return sb.toString();
}
}
class FaceBookUser {
String userName;
UserBasicInfo userInfo;
ContactInfo contactInfo;
public FaceBookUser(String uName){
this.userName = uName;
}
public void setUserBasicInfo(UserBasicInfo info){
this.userInfo = info;
}
public void setContactInfo(ContactInfo info){
this.contactInfo = info;
}
public String getUserName(){
return userName;
}
public UserBasicInfo getUserBasicInfo(){
return userInfo;
}
public ContactInfo getContactInfo(){
return contactInfo;
}
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("|User|").append(userName).append("|UserInfo|").append(userInfo).append("|ContactInfo|").append(contactInfo);
return sb.toString();
}
static class FaceBookUserBuilder{
FaceBookUser user;
public FaceBookUserBuilder(String userName){
this.user = new FaceBookUser(userName);
}
public FaceBookUserBuilder setUserBasicInfo(UserBasicInfo info){
user.setUserBasicInfo(info);
return this;
}
public FaceBookUserBuilder setContactInfo(ContactInfo info){
user.setContactInfo(info);
return this;
}
public FaceBookUser build(){
return user;
}
}
}
public class BuilderPattern{
public static void main(String args[]){
FaceBookUser fbUser1 = new FaceBookUser.FaceBookUserBuilder("Ravindra").build(); // Mandatory parameters
UserBasicInfo info = new UserBasicInfo("sunrise","25-May-1975","M");
// Build User name + Optional Basic Info
FaceBookUser fbUser2 = new FaceBookUser.FaceBookUserBuilder("Ravindra").
setUserBasicInfo(info).build();
// Build User name + Optional Basic Info + Optional Contact Info
ContactInfo cInfo = new ContactInfo("[email protected]","1111111111","2222222222");
FaceBookUser fbUser3 = new FaceBookUser.FaceBookUserBuilder("Ravindra").
setUserBasicInfo(info).
setContactInfo(cInfo).build();
System.out.println("Facebook user 1:"+fbUser1);
System.out.println("Facebook user 2:"+fbUser2);
System.out.println("Facebook user 3:"+fbUser3);
}
}
出力:
Facebook user 1:|User|Ravindra|UserInfo|null|ContactInfo|null
Facebook user 2:|User|Ravindra|UserInfo|Name:DOB:Gender:sunrise:25-May-1975:M|ContactInfo|null
Facebook user 3:|User|Ravindra|UserInfo|Name:DOB:Gender:sunrise:25-May-1975:M|ContactInfo|email:mobile(H):mobile(W):[email protected]:1111111111:2222222222
説明:
FaceBookUser
は以下の属性を持つ複合オブジェクトです。String userName; UserBasicInfo userInfo; ContactInfo contactInfo;
FaceBookUserBuilder
は静的なBuilderクラスで、FaceBookUser
を含みビルドします。userName
は、FaceBookUser
を構築するための必須パラメータです。FaceBookUserBuilder
ビルドをFaceBookUser
:オプションのパラメータを設定することにより、UserBasicInfo
とContactInfo
この例は、ビルダーから構築された異なる属性を持つ3つの異なるFaceBookUsersを示しています。
- fbUser1は、userName属性のみのFaceBookUserとして構築されました。
- fbUser2は、userNameとUserBasicInfoでFaceBookUserとして構築されました。
- fbUser3は、userName、UserBasicInfo、およびContactInfoでFaceBookUserとして構築されました。
上記の例では、BuilderクラスのFaceBookUserのすべての属性を複製する代わりに、compositionが使用されています。
創造的なパターンでは、まずFactoryMethod
ような単純なパターンから始め、 AbstractFactory
やBuilder
ようなより柔軟で複雑なパターンに移行します。
Java / Lombok
import lombok.Builder;
@Builder
public class Email {
private String to;
private String from;
private String subject;
private String body;
}
使用例:
Email.builder().to("[email protected]")
.from("[email protected]")
.subject("Email subject")
.body("Email content")
.build();
Java 8ラムダ式による高度なビルダーパターン
public class Person {
private final String salutation;
private final String firstName;
private final String middleName;
private final String lastName;
private final String suffix;
private final Address address;
private final boolean isFemale;
private final boolean isEmployed;
private final boolean isHomewOwner;
public Person(String salutation, String firstName, String middleName, String lastName, String suffix, Address address, boolean isFemale, boolean isEmployed, boolean isHomewOwner) {
this.salutation = salutation;
this.firstName = firstName;
this.middleName = middleName;
this.lastName = lastName;
this.suffix = suffix;
this.address = address;
this.isFemale = isFemale;
this.isEmployed = isEmployed;
this.isHomewOwner = isHomewOwner;
}
}
旧い道
public class PersonBuilder {
private String salutation;
private String firstName;
private String middleName;
private String lastName;
private String suffix;
private Address address;
private boolean isFemale;
private boolean isEmployed;
private boolean isHomewOwner;
public PersonBuilder withSalutation(String salutation) {
this.salutation = salutation;
return this;
}
public PersonBuilder withFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public PersonBuilder withMiddleName(String middleName) {
this.middleName = middleName;
return this;
}
public PersonBuilder withLastName(String lastName) {
this.lastName = lastName;
return this;
}
public PersonBuilder withSuffix(String suffix) {
this.suffix = suffix;
return this;
}
public PersonBuilder withAddress(Address address) {
this.address = address;
return this;
}
public PersonBuilder withIsFemale(boolean isFemale) {
this.isFemale = isFemale;
return this;
}
public PersonBuilder withIsEmployed(boolean isEmployed) {
this.isEmployed = isEmployed;
return this;
}
public PersonBuilder withIsHomewOwner(boolean isHomewOwner) {
this.isHomewOwner = isHomewOwner;
return this;
}
public Person createPerson() {
return new Person(salutation, firstName, middleName, lastName, suffix, address, isFemale, isEmployed, isHomewOwner);
}
アドバンストウェイ:
public class PersonBuilder {
public String salutation;
public String firstName;
public String middleName;
public String lastName;
public String suffix;
public Address address;
public boolean isFemale;
public boolean isEmployed;
public boolean isHomewOwner;
public PersonBuilder with(
Consumer<PersonBuilder> builderFunction) {
builderFunction.accept(this);
return this;
}
public Person createPerson() {
return new Person(salutation, firstName, middleName,
lastName, suffix, address, isFemale,
isEmployed, isHomewOwner);
}
}
使用法:
Person person = new PersonBuilder()
.with($ -> {
$.salutation = "Mr.";
$.firstName = "John";
$.lastName = "Doe";
$.isFemale = false;
$.isHomewOwner = true;
$.address =
new PersonBuilder.AddressBuilder()
.with($_address -> {
$_address.city = "Pune";
$_address.state = "MH";
$_address.pin = "411001";
}).createAddress();
})
.createPerson();
参考: https : //medium.com/beingprofessional/think-functional-advanced-builder-pattern-using-lambda-284714b85ed5#.d9sryx3g9