20210725のJavaに関する記事は2件です。

デザインパターン勉強会⑥Prototype

はじめに ZOOM勉強会の議事録です。 「増補改訂版Java言語で学ぶデザインパターン入門」を読んで、プログラム作成・パターンの理解を行います。 第6回はPrototypeパターンです。 Prototype インスタンスの複製を作るパターンです。 役割は以下です。 Prototypeインスタンスを複製するためのメソッドを定めるConcretePrototypeインスタンスを複製するメソッドを実際に実装するクラスClientインスタンスを複製するメソッドを使用して新しいインスタンスを作成する 今回は.NET Frameworkに用意されているICloneableインタフェースを使用します。 そのため、クラス図は以下のようになります。 ICloneable 複製用のメソッドとしてClone()のみが定義されているインタフェースです。 ICloneable Public Interface ICloneable Function Clone() As Object End Interface あとから知りましたが、ディープコピーとシャローコピーの区別ができないため、非推奨のようです。(ICloneableインタフェース) また、More Effective C#にもICloneableは、設計の選択肢を狭めるので避けようと書かれています。 今回は、勉強会の議事録なのでICloneableで実装したものを紹介します。 またJavaとVB.NETで仕様が異なります。 JavaのCloneableインタフェースは、マーカーインタフェースと呼ばれる、メソッドを一つも定義していないインタフェースのようです。 ObjectクラスがClone()を定義しており、通常はCloneNotSupportedExceptionの例外を吐くようになっていますが、Cloneableインタフェースを実装したクラスだけ、例外を吐かずにClone()を呼べるようになっています。 このClone()をオーバーライドすることでPrototypeパターンを実現するようになっており、今回紹介する方法とは異なります。 Prototype 複製を行いたいインタフェースにICloneableを継承させます。 Prototype Public Interface Shape : Inherits ICloneable Function GetX() As Integer Function GetY() As Integer Sub SetX(ByVal x As Integer) Sub SetY(ByVal y As Integer) End Interface ConcretePrototype Prototype役を実装した具象クラスを作成します。 Clone()メソッドで、複製を実装します。 コピー用のインスタンスを生成し、各フィールドを設定します。 参照型のフィールドをディープコピーしたい場合は、そのクラスのClone()を呼び、再帰的に複製を行います。 ConcretePrototype Public Class Rectangle : Implements Shape Private x As Integer Private y As Integer Private width As Integer Private height As Integer Public Sub New(ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer) Me.x = x Me.y = y Me.width = width Me.height = height End Sub Public Sub SetX(x As Integer) Implements Shape.SetX Me.x = x End Sub Public Sub SetY(y As Integer) Implements Shape.SetY Me.y = y End Sub Public Function GetX() As Integer Implements Shape.GetX Return Me.x End Function Public Function GetY() As Integer Implements Shape.GetY Return Me.y End Function Public Function Clone() As Object Implements ICloneable.Clone Dim cloneObj = New Rectangle(Me.x, Me.y, Me.width, Me.height) Return cloneObj End Function End Class Client クローンを使用する側です。 Clone()を呼ぶことで複製を作ることができました。 Client Sub Main() Dim r1 As Shape = New Rectangle(1, 2, 3, 4) Dim r2 As Shape = r1.Clone() r1.SetX(2) Console.WriteLine(r2.GetX()) ' 1 Console.WriteLine(r1.GetX()) ' 2 End Sub ICloneableによるPrototypeパターン メリット データを複製するだけなので1から作るより簡単である場合がある 外からではコピーできないprivateフィールドもコピーできる 呼び出し側はクローンメソッドを呼ぶだけで複製を得られる デメリット ディープコピーされるのかシャローコピーされるのかわからない すべての参照型のフィールド変数と派生クラスがICloneableインタフェースを実装していないといけなくなり、階層構造全体がICloneableを実装する必要がある 余談 More Effective C#では、すべてのクラスにICloneableを実装する代わりにClone用のprotectedコンストラクタを使用する方法が紹介されていました。 この方法は、先程のデメリットの「階層構造全体がICloneableを実装する必要がある」という点を回避する手段になっています。 基底クラスは、以下のように派生クラスが複製に使用するprotectedコンストラクタを実装します。 BaseType Public Class BaseType Private label As String Private values As Integer() Protected Sub New() label = "class name" ReDim values(10) End Sub '派生クラスが複製に使用 Protected Sub New(ByVal right As BaseType) label = right.label values = CType(right.values.Clone(), Integer()) End Sub End Class コピー用のコンストラクタは、引数に自クラスを受け取って、フィールドをコピーします。 また、派生クラスで複製のために使用することを目的としているので、アクセス修飾子をprotectedにしています。 このようにすることで、すべての派生クラスにICloneableの実装を強制せず基底クラス部分の複製を返すことができます。 以下が複製をサポートする場合の派生クラスです。 Derived Public NotInheritable Class Derived : Inherits BaseType : Implements ICloneable Private dValues As Double() Public Sub New() ReDim dValues(10) End Sub '基底クラスの複製用コンストラクタを使用して値をコピー Private Sub New(ByVal right As Derived) MyBase.New(right) dValues = CType(right.dValues.Clone(), Double()) End Sub Public Function Clone() As Object Implements ICloneable.Clone Dim rVal = New Derived(Me) Return rVal End Function End Class 複製機能を持たせたい派生クラスには、上のようにICloneableを実装します。 ICloneableを実装する派生クラスは、継承されると以降全ての派生クラスとそのフィールドのクラスにICloneableを実装しなければならなくなるので、NotInheritableとします。 そして、こちらも複製用のコンストラクタを用意し、Clone()で使用します。 このようにすることで、必要なクラスにのみICloneableを実装し、複製機能をもたせることができます。 まとめ 今回はPrototypeパターンについて学びました。 「増補改訂版Java言語で学ぶデザインパターン入門」自体がJavaの本であったため、CloneableとICloneableで使い方が異なりました。 デザインパターンができてから数年経っているため、各パターンの問題点も議論されているようです。 そういった点も考慮して勉強会で議論できるとよりよい勉強になると思いました。 また、Javascriptは、プロトタイプベースのオブジェクト指向と言われており、今回勉強したプロトタイプパターンが関わっているようなので、こちらもいつか勉強できたらと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Springboot+Tymeleaf+Mybatis+MySQLでのCRUD処理実装(基本)

学習のためのCRUDの基本実装をしました。備忘録としてまとめました。 画面上にHelloWorld実装まではできるものとして書いてます。 MySQLにはあらかじめテーブルを用意しています。 開発環境 eclipse IDE Java 11 maven Tymeleaf Mybatis MySQL 設定ファイルの編集 application.propertiesにmySqlとの接続情報を記述 spring.datasource.url = jdbc:mysql://localhost/データベース名?serverTimezone=Asia/Tokyo spring.datasource.username = root spring.datasource.password = データベースのパスワード spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver spring.mvc.hiddenmethod.filter.enabled: true Pom.xmlへの依存関係の記述(プロジェクト生成時依存関係セットに足りないものを追加) <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>SymphogearTest</artifactId> <version>0.0.1-SNAPSHOT</version> <name>SymphogearTest</name> <description>project for Spring Boot</description> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>ture</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>  実装コード コントローラークラス(画面操作と処理の実行をつなぐクラス) package io.spring.gungnir.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import io.spring.gungnir.dto.UserSearchRequest; import io.spring.gungnir.entity.User; import io.spring.gungnir.service.UserService; @Controller //コントローラークラスの宣言 public class UserController { @Autowired //サービスクラスのnewを自動でしてくれるアノテーション UserService userService; /* * URL(localhost:8080/user)でアクセスでトップ画面の表示 */ @GetMapping(value = "/user") //このURLにマッピングされておりGetするとメソッドが動く public String display() { return"top"; //top.htmlの画面を表示 } /* * 一件検索するためのフォームを表示 */ @GetMapping(value = "/user/search") //トップ画面でto search formを押下した時の画面遷移 public String displaySearch() { return"player_search"; //player_search.htmlの画面を表示 } /* * 一件検索の実行 */ @RequestMapping(value = "/user/id_search", method = RequestMethod.POST )    //searchボタンを押され/user/id_seachが呼ばれたとき動く。フォームに入力されたIDをpostで受け取る  public String search(@ModelAttribute UserSearchRequest userSearchRequest, Model model){   //@ModelAttributeがpostされたリクエストをuserSearchReqesutに格納する(同時にnewする)   User user = userService.search(userSearchRequest); //サービスクラスのメソッドにuserSerchReqestを引数として渡す model.addAttribute("user_info",user);     //サービスクラスメソッドの結果をuserに詰める。テンプレートで使うキーとして”user_info”を指定   return"player_search"; //player_search.htmlの画面を表示 } /* * 全件表示 */ @PostMapping(value = "/user/list") //player listボタンで/user/listが呼ばれたとき動く public String getUserList(Model model) { List <User> list = userService.searchAll(); //リスト型でサービスクラスのインスタンスメソッドを呼び出す(リスト取得の指示だし) model.addAttribute("users_info", list); //結果をlistに詰める。(users_infoがキー) return "list"; //list.htmlの画面を表示 }  /*新規プレイヤー登録画面を表示 * 画面遷移のみ */ @PostMapping(value = "/user/add") //to sing up formのボタンで/user/addが呼ばれたとき動く public String displayAdd() { return "add_player"; //add_playerの画面を表示 }  /*登録の実行 * 登録情報の表示 */ @RequestMapping(value = "user/add_comp", method = RequestMethod.POST )    //sign upが押され/user/add_compが呼ばれた時動く。フォームの内容をpostで受け取る  public String create(@ModelAttribute UserSearchRequest userAdd, Model model) { //postされたリクエストをusesrAddに格納+new   userService.create(userAdd);//追加処理の実行をサービスクラスに指示 User user = userService.createCheck(userAdd); //追加した情報をセレクトしとってくるよう指示 model.addAttribute("user_add", user); //セレクト結果をuserに詰める。(user_addをキー) return "add_comp"; //add_comp.htmlの画面を表示 }  /* * 編集画面への遷移 */ @PutMapping(value = "/user/conf/id={id}") //updateが押されたとき動く public String editSelect(@PathVariable("id")String id ,Model model){   //@PathVariableでURL内のidを引数に取得 User user = userService.editSelect(id); //取得したidを渡してセレクト指示 model.addAttribute("user_select",user); //セレクト結果をuserに詰める。(user_selectがキー) return "conf_player"; //conf_player.htmlを表示 }    /*   *編集の実行   */ @RequestMapping(value = "/user/edit/id={id}", method = RequestMethod.POST) //editボタンが押されたとき動く public String update(@ModelAttribute UserSearchRequest edit) { userService.update(edit); //postされたリクエストが格納されたeditを引数にupdateメソッド呼び出し return "edit"; //edit.htmlを表示 }  /*削除の実行 * */ @DeleteMapping(value = "user/delete/id={id}") //deleteボタンが押されたとき動く public String displayDelete(@ModelAttribute UserSearchRequest delete) { userService.deleteOne(delete); postされたリクエストが格納されたdeleteを引数にdeleteOneメソッド呼び出し return "delete"; //delete.htmlを表示 } } ``######サービスクラス(画面からのリクエストを実行するクラス) ```java package io.spring.gungnir.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import io.spring.gungnir.dto.UserSearchRequest; import io.spring.gungnir.entity.User; import io.spring.gungnir.repository.UserMapper; @Service //サービスクラスの宣言 public class UserService { @Autowired //userMapperを自動でnewしてくれる private UserMapper userMapper;   /*   * 一件検索表示 */ public User search(UserSearchRequest userSearchRequest) { return userMapper.search(userSearchRequest); //検索結果をリターンで受け取る }  /* * 全件表示 */ public List<User> searchAll(){ return userMapper.searchAll(); //全件のリストをリターンで受け取る } /* * 新規追加処理 */ public void create(UserSearchRequest userAdd) { userMapper.create(userAdd); }  /* * 追加情報を画面表示 */ public User createCheck(UserSearchRequest userAdd) { return userMapper.createCheck(userAdd); }    /* * 編集対象の検索 */ public User editSelect(String id) { return userMapper.editSelect(id); }  /* * 編集実行 */ public void update(UserSearchRequest edit) { userMapper.edit(edit); }  /* * レコード情報削除 */ public void deleteOne(UserSearchRequest delete) { userMapper.deleteOne(delete); } } マッパークラス(データベースを操作するクラス) package io.spring.gungnir.repository; import java.util.List; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import io.spring.gungnir.dto.UserSearchRequest; import io.spring.gungnir.entity.User; @Mapper //マッパークラスの宣言 public interface UserMapper { /* * プレイヤー情報検索 */ @Select("SELECT * FROM symphogear_players WHERE id = #{id}") User search(UserSearchRequest user); /* * 全件表示 */ @Select("SELECT * FROM symphogear_players") List<User> searchAll(); /* * 新プレイヤー情報登録 */ @Insert("INSERT INTO symphogear_players(id,name,symphogear_name)" + "VALUES (#{id},#{name},#{symphogear_name})") void create(UserSearchRequest userAdd); /* * 新追加情報表示 */ @Select("SELECT * FROM symphogear_players WHERE id = #{id}") User createCheck(UserSearchRequest userAdd); /* * 編集用の情報セレクト */ @Select("SELECT * FROM symphogear_players WHERE id = #{id}") User editSelect(String id); /* * 情報編集 */ @Update("UPDATE symphogear_players " + "SET name = #{name}, symphogear_name = #{symphogear_name} " + "WHERE id = #{id}") void edit(UserSearchRequest edit); /* * レコード削除 */ @Delete("DELETE FROM symphogear_players WHERE id = #{id}") void deleteOne(UserSearchRequest delete); } DTOクラス(画面からのリクエストデータを格納するクラス) package io.spring.gungnir.dto; import java.io.Serializable; import lombok.Data; @Data //セッター、ゲッターを記述しなくても使用できるようになる public class UserSearchRequest implements Serializable{ private String id; private String name; private String symphogear_name; } エンティティクラス(データベースから取得したデータを格納するクラス) package io.spring.gungnir.entity; import lombok.Data; @Data public class User { /* * ユーザー情報 entity */ private String id; private String name; private String symphogear_name; } 以上 長くなったので 画面周りのhtmlは次の記事→https://qiita.com/dende-h/items/9501d07c0e4088a0018c 感想・気づいたこと 一通りまとめたことで、処理の流れがつかめた。 途中で、実際には不要なコードがわかりより簡潔なコードにできた。 ここから入力チェックや例外処理など機能を増やしていく。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む