プロが教えるわが家の防犯対策術!

「オブジェクト志向」の考えかたで質問します。
いろいろと調べると、

・繼承
・カプセル化
・ポリモーフィズム
を総称したのが、「オブジェクト」志向と理解しています。

このとき、
たとえば、
指定するクラスの生徒の情報をとりだすようなソースをつくりたい。
仮に、以下をかんがえてみました。
DB処理は、省いてます。

//実行DAOクラス
public class StudentDAO extends StudentDBAccessor{

//指定するクラスに属する生徒をとりだす
public List getStudentList(int classNumber){

return super.getStudentList();

}
//sql文生成
protected String createSqlSelectStudentList(){
StringBuffer sb = new StringBuffer();
return sb.toString();

}
//キーワードを設定
public void setDataSqlStudentList(){

}
}

public abstract StudentDBAccessor extends DBConnector{

protected List getStudentList(){

  //DBそうさ
}

protected abstract String createSqlStudentList();
protected void setDataSqlStudentList(int classNumber);


}

//DB接続クラス
public class DBConnector{
  //省略
}

//Beanクラス
public class StudentFormBean{

private int studentNumber;
private String studentName;

public void setStudentNumber(int number){
this.studentNumber = number;
}
public int getStudentNumber(){
return studentNumber;
}
}

よろしくおねがいします。

A 回答 (7件)

コレクションというのは、List,ArrayList,


Set,HashSet etc...の亊を言うのでしょうか?

そうです。


List<Human> heightList = new ArrayList<Human>(new HeightComparator());
の系式で表す亊はできるのでしょうか?

ArrayListではできません。


このときのアルゴリズムをいれかえる動作というのは、
ソースでいうと、・・・の部分の亊をいうのでしょうか?

そうです。


一方、これをListでも実現する亊はできるのでしょうか?

そもそもなぜListでやろうと思った(Listでやる必要性は?)のでしょうか?まずListやSetの使い分けを学んだほうがいいと思います。


また、Collectionを仮に、実装するクラスを作ってみたのですが、
一般に、このような形で使われる亊はあったりするのでしょうか?

普通はCollectionを直接実装するようなことはしません。すでに用意されているもの(ArrayList、LinkedList、HashSet、TreeSet 、HashMap etc...)で間に合うからです。

また、
public class HeightComparator2 extends HeightCollection
implements Comparator<Human2>

という継承もおかしいです。CollectionとComparator両方を継承してしまっては、Strategyパターンの意味がない(というかStrategyパターンではない)です。Comparator(並べ替えるためのロジック)を入れ替えることによって、並べ替え方を変えることができるというのがポイントです。CollectionとComparator両方を混ぜてしまっては、あるひとつの方法でしか並べ替えることができない、柔軟性がないものになってしまいます。

この回答への補足

ありがとうございます。

それで、ソースを考える前にきいておきたいのですが、
SortedSet<Human> heightSortSet = new TreeSet<Human>(new HeightComparator());
の、heightSortSet , TreeSetをそれぞれクラスに変えて考えてみればいいのでしょうか?

たとえば、仮に、
クラスをキーに、そこに属する生徒をとりだす場合と、そのクラスの教師をとりだす場合とでわけた時
それぞれのdb取得方法を別クラスに設定して、
new HeightComparator()のような役

SortedSet 、DBConnectionとかといった、dbにアクセスクラス

といった考え方はできるのでしょうか?

どうしても、ソースを上手く考えられません。


よろしくお願いします。

補足日時:2010/05/20 17:36
    • good
    • 0

最初の質問と少しずれてきてしまいましたが・・・とりあえずStrategyパターンについて解説しておきます。


sampleだからしかたないですが、リンク先のページは単純にしすぎて逆にわかりづらいですね・・・MyClassが単純すぎてほとんど意味がありません。

以下にもう少し実際に近いsampleを載せます。Strategyのポイントは"アルゴリズムを入れ替える"ということです。
TreeSetのComparatorに違うものを使用することによって、体重別と身長別という異なる順でソートしています。この場合、最初からTreeSetはStrategyパターンで設計されています(途中でComparatorを入れ替えられないので、厳密にはStrategyパターンとは言えないかもしれませんが)
このように、そもそもJavaのコレクション自体の多くのところでデザインパターンが使用されているので、ソースコードを読んでみるといいでしょう。



public class CompareTest {

public static void main(String[] args) {

Set<Human> people = new HashSet<Human>();

people.add(new Human("一郎",170,60));
people.add(new Human("二郎",165,55));
people.add(new Human("三郎",175,58));
people.add(new Human("四郎",160,67));
people.add(new Human("五郎",180,62));

SortedSet<Human> heightSortSet = new TreeSet<Human>(new HeightComparator());
SortedSet<Human> weightSortSet = new TreeSet<Human>(new WeightComparator());

heightSortSet.addAll(people);
weightSortSet.addAll(people);

System.out.println("身長順");
for(Human h:heightSortSet){
System.out.println(h);
}

System.out.println("\n体重順");
for(Human h:weightSortSet){
System.out.println(h);
}
}

}

class Human{
Human(String name,int height,int weight){
this.name = name;
this.height = height;
this.weight = weight;
}
public final String name;
public final int height;
public final int weight;
@Override
public String toString(){
return "名前:"+name+" 身長:"+height+" 体重:"+weight;
}

}

//身長で比較するComparator
class HeightComparator implements Comparator<Human>{
@Override
public int compare(Human h1,Human h2) {
return h1.height - h2.height;
}
}

//体重で比較するComparator
class WeightComparator implements Comparator<Human>{
@Override
public int compare(Human h1,Human h2) {
return h1.weight - h2.weight;
}
}

この回答への補足

解答ありがとうございます
調べてみました。
コレクションというのは、List,ArrayList,
Set,HashSet etc...の亊を言うのでしょうか?

また、
List<Human> heightList = new ArrayList<Human>(new HeightComparator());
の系式で表す亊はできるのでしょうか?

このときのアルゴリズムをいれかえる動作というのは、
ソースでいうと、

SortedSet<Human> heightSortSet = new TreeSet<Human>(new HeightComparator());
SortedSet<Human> weightSortSet = new TreeSet<Human>(new WeightComparator());
の部分の亊をいうのでしょうか?

一方、これをListでも実現する亊はできるのでしょうか?
その場合、Collectionを実装する必要があるように感じるのですが。。。

以下、一度考えてみました。
//HeightComparatorを変えたソース
public class HeightComparator2 extends HeightCollection
implements Comparator<Human2>
{

public int compare(Human2 h1,Human2 h2) {

System.out.println("test--- during //// ");
System.out.println("h1.height +"+h1.height + "/h2.height:" + h2.height);
return h1.height - h2.height;
}
}

//Collectionを実装したクラス
public class HeightCollection implements Collection {

}

public class SampleRun{
public static void main(String[] args){

//Listに入れる型を指定しておく
List<Human2> list = new ArrayList<Human2>();
list.add( new Human2("right", 120,24) );

//万一、Listにクラスインスタンスを入れようとしたら、Collectionでしか対応しない
// List<Human2> heightList = new ArrayList <Human2>( (Collection) new HeightComparator2());
List<Human2> heightH = new ArrayList<Human2>();
heightH.addAll(list);

heightH = new ArrayList<Human2>();
heightH.addAll(list);

heightH = new ArrayList<Human2>(new HeightComparator2());
for(Human2 ha: heightH) {
System.out.println("ha::: " + ha);
}
}
}

補足:
また、Collectionを仮に、実装するクラスを作ってみたのですが、
一般に、このような形で使われる亊はあったりするのでしょうか?

よろしくお願いします。

補足日時:2010/05/18 18:27
    • good
    • 0

補足についての回答



2については、やはりこのようにつかうなら、インスタンス変数はfinalにして、コンストラクタで設定するほうがいいでしょう。
setterを用意するということは、初期化に値を定めることができない、もしくは値を書き換える(内部の状態を変える)必要があるときです。nullチェックが必要になってしまいますし。

3については・・・
多重継承が出来る言語なら、ある機能を他のクラスで定義して、継承することによって取り入れるという使い方は可能なのですが、Javaの場合だとちょっとまずいですね。プログラムが大きくなってきた場合、このような使いかたをしていると、逆にプログラムの構造を変更するのに、大変になる場合があります。
他の方もいっていますが、Javaの場合は特に、本当にis-a関係がある時だけ継承を使いましょう。

あと細かいとこですが、今のままだと切断の前にconがnullの可能性があるので、nullチェックしないといけません。


生徒は実際に存在するものなので、それをClassにするというのは結構思いつきやすいんですが、データベースの場合は、プログラムを作る人が、考えなければいけないので、難しいです。

私が前Javaでデータベース接続のクラスを作ったときは、Strategyパターンで作りました。DBの接続、データ取得の汎用的な処理をDB接続用のクラスにもたせて、具体的な取得方法など別に用意して、そのクラスに渡すというやりかたです。

この回答への補足

遅くなりました。
解答ありがとうございます。

strategyパターンについて調べてみました。
その際、
http://www.techscore.com/tech/DesignPattern/Stra …
を参考にしたのですが、
この場合、ageとheightとweightそれぞれを比較するクラスを別クラスとして実装していますが
これを実際に実行しようとしたら、
どう考えるべきなのでしょうか?
1)Humanクラスにデータを設定
2)Comparatorを実装するeightComparator,AgeComparator,WeightComparatorクラスに渡す

この時のa),b)の宣言のちがい
a)再利用性の意味がない = ComparatorをHeightComparatorに実装する時にメンテナンスの見通しがいる
b)再利用性の意味あり = ComparatorをHeightComparatorに実装する時にメンテナンスの見通しがいらない

また、b)とc)のちがいは、クラスとしてかメソッドとして取るか

と考えられるのでしょうか?


使用クラス
HeightComparator,AgeComparator,Human,
SampleClass

ーーーー SampleClass -------------
public class SampleClass {
private Comparator comparator = null;
public SampleClass(Comparator comparator){
this.comparator = comparator;
}
public int compare(Human h1,Human h2){
return comparator.compare(h1,h2);
}

}

ーーーー 実行クラス ---------------
public class SampleRun {

public static void main(String[] args) {

//Comparator cp = null ;
SampleClass sc = new SampleClass(cp);
Human hm = new Human("かりん",161,48,15);
Human hm2 = new Human("ちぐさ",157,50,15);

int mc = new AgeComparator().compare(hm, hm2);
System.out.println("mc:"+mc);

a)
int mc2 = new HeightComparator().compare(hm,hm2);
System.out.println("mc2:"+mc2);
b)
cp = new HeightComparator();
System.out.println("cp:" + cp.compare(hm, hm2) );
c)
int mc3 = new HeightComparator().compare(hm, hm2);
System.out.println("mc3.compare:"+mc3);
}
}

結果:
h1.age:15/h2.age:15
mc:0
mc2:1

また、このページで「SampleClassは以下のように記述することができます」
とあるが、MyClassとなっている。
これはこれで正しいのでしょうか?

よろしくお願いします。

補足日時:2010/05/17 17:35
    • good
    • 0

プログラムも


実世界の構造にあわせたほうが
汎用性の高いプログラムが出来るんでは
ないか?と感じてます。
っていうかそういう場合がほとんどです。

1)の
StudentがstudentNumberを持つ
StudentがstudentNameを持つ
これはHAS-Aとして成り立つと思います。
実世界でも生徒が名前や出席番号を持っています。ね。

2)の
StudentFormBean#setStudentClassの
引数型が無いのでよくわかりません

super.sutudentClassも見えないのよくわかりません

生徒データを管理するのは先生?っていうのをコード化し
質問の
>>2)生徒は、クラスに属する = IS-Aで考えられないか。
をもう少し紐解くと

生徒は、クラスに属する
の逆を言うと
クラスは生徒達を持つになりませんか?
1クラスに付き生徒1人なんて事はまずないので。。。
っていう事は、
class クラス {
private 生徒達 sutudents;
}
ってなりますね。
生徒データを管理するものが必要だとしたら
管理するオブジェクトが生徒を持つのが
妥当かと、
そこで生徒達クラスの登場となります。
生徒達クラスが複数の生徒を管理する手段を提供すればよいと思います。
class 生徒達 {
pivate 生徒[] sutudents;
//例えば以下のように使いやすくなります。
private int get生徒人数 { return this.sutudents.length;}
private int get男子生徒人数{
//this.sutudensを調べ返す
//何で男子人数が判るかは下の[class Student extends 人]でわかります
}
}
もっと言うとクラスを担当するのは先生なので
class 先生 {
private クラス clazz;
}
ここまでで先生がクラスを担任し、クラスには生徒達が存在し、
クラス単位で生徒達を管理するっていう形が出来上がったと思います。

じゃぁいつIS-Aが成り立つのか思いました?
例えば生徒も先生も人ですね。
以下例えばですよ。
class Student extends 人 {}
class 先生 extends 人 {}
class 人 extends 生き物{
private 性別;
pivate 年齢;
}
class 生き物{
private 命;
}
ってすれば
先生クラスを見れば先生は人なんですから性別も年齢も
判断できることが保障されますね。
人である以上生き物なんですから
命があることもコード上保障されますね。

先生をインスタンス化する時点で保障すべき
事にはIS-Aが成り立つ、
HAS-AではOBJECT_Aが持つOBJECT_Bへの
アクセス手段をオブジェクトAが実装しない限り保障されませんよね。
そのフィールドをカプセル化するのはもちろん、
アクセス手段・インスタンス化手段も限定できる
それがカプセル化。
IS-Aではそのオブジェクトがインスタンス化された時点で
親オブジェクトの機能は保障されることになります。
それが継承であり、それを抽象化・Interface化することが
ポリモーフィズムってことかな。

カプセル化できてなければ多能性もなくなるし、
多能性が無ければオブジェクト指向でなくて
手続き型のプログラムでよいと思うし
全てある程度できて使えるプログラムかなっておもうよ。


DBのAccessor・DBConnectorの話になると、
DBConnectorは接続先情報を持つ。
AccessorはDBConnectorを持つ
っていう風にしたほうが汎用性が高くなんじゃないかな?
今のままだと
StudentDBAccessor IS DBConnectorだから
DBConnectorであるStudentDBAccessorしか拡張できない。

以下みたいな考えが理解できると
汎用性の多少あるプログラムが書けるかも。

class DBAccessor extends Accessor{
private DBConnector connector;
}
class StudentDBAccessor extends DBAccessor{
private QueryExecuter executer;
}
class DBConnector extends Connector{
private DataSource ds;
}
DataSource - 接続先情報
QueryExecuter - SQL実行クラス
と理解してください。
長文すみません
わからないことあったらまた聞いてください。

この回答への補足

解答ありがとうございます。
遅くなりました。

この時なのですが、
DBConnectorを共用化しようとした場合どうすべきなのでしょうか?
private DBConnector connector;
のように定義したら、毎回初期化される气がする。。。

一度サンプルに関して、考えてみました。
それで、
QueryDBAccessorクラスは、
public class QueryExecuter
implements StudentInterface {

private Connection con;

//指定するクラスに属する生徒をとりだす
public void getStudents(){

Statement stmt = null;
try{
String sql = createSqlSelectStudents();
con.prepareStatement(sql);
stmt.setDataSelectStudent();
ResultSet rs = stmt.executeQuery();
//dbから取りだした結果をよびだしクラスに返す

while(rs.next()){
//?ここでとりだす?
}

}catch(Exception e){

}
}

protected abstract String createSqlSelectStudents(){
//sql文
return sql文.toString();
}

protected abstract void setDataSelectStudent(){

}
}

public interface StudentInterface{

protected abstract String createSqlSelectStudents();
protected abstract void setSelectStudent();
}

な感じでしょうか?

また、
DBAccessor 色んなクラスからよばれる
ex) 先生を新たに追加する、etc...

DBConnector dbに接続する際の処理
全体に共通するもの

StudentDBAccessorは、生徒に関わる処理
っていう感じでしょうか?

できたら、処理のイメージ的なものを出してもらいたいのですが。。。

よろしくお願いします。

補足日時:2010/05/18 22:17
    • good
    • 0

IS-A, HAS-Aの考え方を持つと抽象化もしやすく


オブジェクト思考が一歩進むと思います。
例えば
人は名前を持つ(HAS-A)
人は動物である(IS-A)

これを抽象化して使いやすく
砕いていくと結構汎用性の高いオブジェクト指向でる
プログラミングができると思います。

簡単に言うと
IS-Aなら継承で表す
HAS-Aならフィールドに保持する
って言うような設計にすると
汎用性の高いプログラムになると思います。

以上を踏まえて
以下は、コードを見て直感的に書きますが。

今のサンプルコードの考え方だと
StudentDBAccessor IS DBConnector になってるけど
DBConnectorは接続先を持つ訳で、
StudentDBAccessor extends DBConnectorでは
接続先DBが増える都度クラスの増設・改修などが
増えてしまいそうと思いました。

とりあえず簡単に意見でした。
もし、もうすこし噛み砕いたほうが
よいなら再度質問してください。

この回答への補足

回答ありがとうございます。

たとえば、
今の処理で、

1)生徒は、名前、学生Noをもつ = HAS-Aの考えかた
2)生徒は、クラスに属する = IS-Aで考えられないか。

のようにするとしたら、IS-Aを使用することはできるのでしょうか?

その場合、イメージ的に
1)
public class StudentFormBean{
private int studentNumber; //フィールドとしてもつ
private String studentName; //フィールドとしてもつ
}

2)生徒のクラスを管理する
public class StudentClass{

public void setStudentClass(){

}
//生徒のクラスデータのとりだし
public StudentClassFormBean getStudentClass(){
}

}
生徒のデータを管理する
public class Student extends StudentClass{

  public void setStudent(StudentFormBean sFr){
  
   //クラスデータをとってきて、生徒データを追加
   super.studentClass();
  }

}

として考えるとまた違ってくるということなのでしょうか?



接続先DBが増える都度クラスの増設・改修などが
増えて。。。

その場合どう変えるべきなのでしょうか?

>もし、もうすこし噛み砕いたほうが。。

おねがいします。


よろしくおねがいします。

補足日時:2010/05/13 05:10
    • good
    • 0

>・繼承


>・カプセル化
>・ポリモーフィズム
これはオブジェクト指向プログラムを実現するための手段であって、これ自体がオブジェクト指向ではありません。
これらの機能を使ってもオブジェクト指向とは呼べないプログラムを作る事は可能です。

オブジェクト指向はデータとデータを操作するプログラムのセットを一つの独立した固まり(オブジェクト)として扱う思想です。

>指定するクラスの生徒の情報をとりだすようなソースをつくりたい。
機能を中心に考えるのではなくデータを中心にして、データとデータを操作する為の機能のかたまりで考えると良いです。

この回答への補足

>>指定するクラスの生徒の情報をとりだすようなソースをつくりたい。
>機能を中心に考えるのではなくデータを中心にして、データとデータを操作する為の機能のかたまりで考えると良いです。

たとえば
今の場合、簡単にまとめてみると。

ーーデータ --
生徒は、名前、生徒Numberをフィールドとしてもつ
生徒は、クラスに属している
生徒クラスは、クラス名、クラスNumberをフィールドとしてもつ

ーー機能ーー
クラスは、生徒を管理するクラスと、生徒を管理するクラスがある。

1)生徒を新たに追加するには、クラスを指定
2)クラスNumberから、クラスを特定するため(結果をbooleanでとりだす)の処理がいる = StudentClassDAO
3)2でクラスがあれば、そのクラスに属する生徒を追加 =StudentDAO

と考えてみる、ということでしょうか?

よろしくお願いします。

補足日時:2010/05/13 05:55
    • good
    • 0

・繼承


・カプセル化
・ポリモーフィズム
を総称したのが、「オブジェクト」志向と理解しています。

というのは間違ってないですが、オブジェクト指向といっても詳細な機能は言語によって違うので、(たとえば継承がなくてもオブジェクト指向という場合もあるし、Javaは基本型はオブジェクトではなく、ポリモーフィズムができないなど・・・)
Javaをやるなら、Java的にわかりやすい、スタンダードなコーディングスタイルかどうかを意識した方がいいと思います。

それで、コードを見た感想としたは、だいたいはいいと思います。気になった点としては、

1.getStudentListの戻り値の型がListになっていますが、ジェネリックを使いましょう。List<StudentFormBean>とするべきだと思います。また、Listということは順序づけられているということになりますが、どのように順序付けて返すつもりなのでしょうか?順序付ける必要がないなら、Set、あるいはCollection型で返すという選択肢もあります。

2.StudentFormBeanについてですが、studentNameはいつ設定するのでしょうか?(privateのインスタンス変数なのにコンストラクタもないし、setterもない)また、studentNumberは変更できるようになっていますが、変更する必要があるのでしょうか?もし変更する必要がないなら、getterもsetterもなくして、インスタンス変数はfinalにして、コンストラクタで設定するようにしましょう。どちらにしろStudentFormBeanのインスタンスを、インスタンス変数をすべて空で作成する場面はあまり無いと思うので、引数ありのコンストラクタは必要でしょう。

3.DBConnector→StudentDBAccessor→StudentDAOという継承関係になっていますが、このような継承関係にした意図というか意味がいまいち見えてきません。たとえばStudentDBAccessorは抽象クラスになっていて、抽象メソッドを定義していますが、子クラスにおいてどのような実装を期待してこのような定義をしたのでしょうか?

また、DBConnectorから継承していますが、コンポジションや委譲という関係で作成するという方法もあります。コンポジションは聞いたことがありますか?DBConnectorの中身が省略されていて、役割がわからないので、今の設計がいいとも悪いとも言えませんが・・・

この回答への補足

解答ありがとうございます。
全て書ききれないので、一部分先に、書きます。
1
今は、studentNumberでソートする形で考えています
sql文で、ascを使用したパターンです。

2
nameは、numberと同じタイミングで設定します。
=単に、生徒のデータをとる時に設定する

UserDAOクラスのgetStudentListは、
public List getStudentList(){
UserFormBean uFr = new UserFormBean();
List studentList = new ArrayList();
//dbからデータとりだし
ResultSet rs = super.stmt.executeQuery();
while(rs.next()){
int num = rs.getInt("num");
String name = rs.getString("name");
uFr.setStudentNumber(num);
uFr.setStudentName(name);
studentList.add(uFr);
uFr = new UserFormBean();
}
return studentList;
}
の処理を考えています。

3
理由としては、処理を分割する亊で再利用しやすくするため、と考えていたのですが。

DBConnectorの内容としては、

public class DBConnector {

public static Connection con = null;
protected PreparedStatement stmtDb = null;

/**
* データベースを開放する
*/
public void DbConnect() {
System.out.println("connecto");
try{
Class.forName("org.postgresql.Driver");
this.con = DriverManager.getConnection("jdbc:postgresql:shopping","postgresql","test");

}catch(Exception e) {
System.out.println("ms:"+e.getMessage());
System.out.println("error:"+ e.getStackTrace());
}
}
/**
* db接続を実行する
*/
public final void Connect() {

//db接続を確認
if(this.con == null) {
this.DbConnect();
}
}
/**
* dbを切断する
*/
public void dbClose() {

try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
です。
意図としては、一度、dbに接続すると何度もアクセスしないですむようにしています。

ヨロシクおねがいします。

補足日時:2010/05/11 20:46
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!