回答編集履歴

2

デレゲートパターンの説明を追加

2020/02/26 23:50

投稿

quadii.shii
quadii.shii

スコア257

test CHANGED
@@ -1,3 +1,5 @@
1
+ # デレゲートパターンを使う場合
2
+
1
3
  ```Kotlin
2
4
 
3
5
  class MainActivity : AppCompatActivity() {
@@ -243,3 +245,265 @@
243
245
 
244
246
 
245
247
  という感じです。
248
+
249
+
250
+
251
+ ## デレゲートとは
252
+
253
+ デレゲート(delegate)とは委譲(実行をお願いする)という意味があり、このケースだと「MainActivityが自分の持つrefreshという関数を実行してくれるようAddDialogにお願いする」という意味になります。
254
+
255
+ AddDialogの`onAdded`という変数に`MainActivity::refresh`という関数の参照を保持しており、実際の呼び出し`invoke`が行われるのはAddDialog内ですが、実行されるのはMainActivity側の関数なので、MainActivity内のリソースを用いて行われます。
256
+
257
+
258
+
259
+ # デレゲートを使わないパターン
260
+
261
+ ちょっと特殊ですがデレゲート使わないパターンも載せておきます。
262
+
263
+ デレゲートパターンでも実際にやっていることはこれと同じになります。
264
+
265
+
266
+
267
+ ```Kotlin
268
+
269
+ class MainActivity : AppCompatActivity() {
270
+
271
+
272
+
273
+ private val _helper = DatabaseHelper(this@MainActivity)
274
+
275
+
276
+
277
+ override fun onCreate(savedInstanceState: Bundle?) {
278
+
279
+ super.onCreate(savedInstanceState)
280
+
281
+ setContentView(R.layout.activity_main)
282
+
283
+
284
+
285
+ val fab: View = findViewById(R.id.fab)
286
+
287
+ fab.setOnClickListener(object: View.OnClickListener{
288
+
289
+ override fun onClick(v: View?) {
290
+
291
+ // AddDialogを初期化するときMainActivity本体を渡してしまう。
292
+
293
+ val dialog = AddDialog(this@MainActivity)
294
+
295
+ // 追加があるとリストが再び初期化される
296
+
297
+ // デレゲートパターンは使わない
298
+
299
+ // dialog.onAdded = this@MainActivity::refresh
300
+
301
+ dialog.show(supportFragmentManager, "AddDialog")
302
+
303
+ }
304
+
305
+ })
306
+
307
+
308
+
309
+ val lv = findViewById<ListView>(R.id.lv)
310
+
311
+
312
+
313
+ // リスト初期化
314
+
315
+ refresh()
316
+
317
+ }
318
+
319
+
320
+
321
+   // 繰り返し使うので関数として切り出した。
322
+
323
+ // AddDialog内で呼び出したいのでprivateを外してクラス外側からアクセスできるようにする。
324
+
325
+ fun refresh() {
326
+
327
+ var itemsList : MutableList<String> = mutableListOf()
328
+
329
+ val db = _helper.writableDatabase
330
+
331
+ val sql = "SELECT * FROM items"
332
+
333
+ var cursor = db.rawQuery(sql, null)
334
+
335
+
336
+
337
+ val idxItems_name = cursor.getColumnIndex("items_name")
338
+
339
+
340
+
341
+ while(cursor.moveToNext()){
342
+
343
+ itemsList.add(cursor.getString(idxItems_name))
344
+
345
+ }
346
+
347
+
348
+
349
+ val adapter = ArrayAdapter<String>(applicationContext, android.R.layout.simple_list_item_1, itemsList)
350
+
351
+ lv.adapter = adapter
352
+
353
+ }
354
+
355
+
356
+
357
+ override fun onDestroy() {
358
+
359
+ _helper.close()
360
+
361
+ super.onDestroy()
362
+
363
+ }
364
+
365
+ }
366
+
367
+ ```
368
+
369
+
370
+
371
+ ```Kotlin
372
+
373
+ // AddDialogのプロパティとしてMainActivityを持たせてしまう。
374
+
375
+ class AddDialog(val mainActivity: MainActivity): DialogFragment(){
376
+
377
+
378
+
379
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
380
+
381
+
382
+
383
+ // デレゲートを使わないパターン
384
+
385
+ // var onAdded: (() -> Unit)? = null
386
+
387
+
388
+
389
+ val editText = EditText(activity)
390
+
391
+ val _helper = DatabaseHelper(activity as Context)
392
+
393
+
394
+
395
+ val builder = AlertDialog.Builder(activity)
396
+
397
+ builder.setTitle(R.string.add_dialog_title)
398
+
399
+ builder.setView(editText)
400
+
401
+
402
+
403
+ builder.setPositiveButton("追加", object :DialogInterface.OnClickListener{
404
+
405
+ override fun onClick(dialog: DialogInterface?, which: Int) {
406
+
407
+ when(which){
408
+
409
+ DialogInterface.BUTTON_POSITIVE->{
410
+
411
+ val addItem = editText.text.toString()
412
+
413
+ if(addItem.length != 0) {
414
+
415
+
416
+
417
+ val db = _helper.writableDatabase
418
+
419
+
420
+
421
+ val sqlCheck = "SELECT * FROM items WHERE items_name = ?"
422
+
423
+ val parms = arrayOf(addItem)
424
+
425
+ val cursor = db.rawQuery(sqlCheck, parms)
426
+
427
+
428
+
429
+ //存在チェック
430
+
431
+ if(cursor.moveToNext() == true){
432
+
433
+ Toast.makeText(activity, "すでに存在します", Toast.LENGTH_SHORT).show()
434
+
435
+ }
436
+
437
+ else{
438
+
439
+ val sqlInsert = "INSERT INTO items (items_name) VALUES (?)"
440
+
441
+ var stmt = db.compileStatement(sqlInsert)
442
+
443
+ stmt.bindString(1, addItem)
444
+
445
+ stmt.executeInsert()
446
+
447
+
448
+
449
+ Toast.makeText(activity, "追加しました", Toast.LENGTH_SHORT).show()
450
+
451
+
452
+
453
+ // ここで直接mainActivityに所属するrefreshを実行する。
454
+
455
+ mainActivity.refresh()
456
+
457
+ }
458
+
459
+ }
460
+
461
+ else{
462
+
463
+ Toast.makeText(activity, "アイテムを入力してください", Toast.LENGTH_SHORT).show()
464
+
465
+ }
466
+
467
+
468
+
469
+ }
470
+
471
+ }
472
+
473
+ }
474
+
475
+ })
476
+
477
+
478
+
479
+ builder.setNegativeButton("キャンセル", object :DialogInterface.OnClickListener{
480
+
481
+ override fun onClick(dialog: DialogInterface?, which: Int) {
482
+
483
+ Toast.makeText(activity, "キャンセルしました", Toast.LENGTH_SHORT).show()
484
+
485
+ }
486
+
487
+ })
488
+
489
+
490
+
491
+ val dialog = builder.create()
492
+
493
+ return dialog
494
+
495
+ }
496
+
497
+ }
498
+
499
+ ```
500
+
501
+
502
+
503
+ かなり乱暴なやり方ですが、これでも動きます。
504
+
505
+ ## このやり方の悪い点
506
+
507
+ AddDialogを他のアプリで使いまわしたいと思ったとき、このMainActivity以外とは使えないことになってしまいます。
508
+
509
+ そのために古来よりインターフェースという素晴らしいパターンがあるのですが(OnClickListenerなどがそれ。論じると長くなるのでやめておきます)、最近の言語では単一の処理だけを委譲(お願い)したい場合は、圧倒的にデレゲートパターンを使います。

1

分かりづらそうなところを修正しました。

2020/02/26 23:50

投稿

quadii.shii
quadii.shii

スコア257

test CHANGED
@@ -230,10 +230,16 @@
230
230
 
231
231
  * AddDialogにデリゲート(コールバック)を追加。
232
232
 
233
+ `var onAdded: (() -> Unit)? = null`
234
+
233
235
  * 追加したデリゲートと同じ型の関数をMainActivity側で代入する。
234
236
 
237
+ `dialog.onAdded = this@MainActivity::refresh`
238
+
235
239
  * Addが発生したとき、デリゲートを実行する。
236
240
 
241
+ `onAdded?.invoke()`
242
+
237
243
 
238
244
 
239
245
  という感じです。