mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-10-15 18:15:54 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d741df1e3a | ||
|
|
3b46af6190 | ||
|
|
d9512df57b | ||
|
|
cd0c4db685 |
No files matched your search
+6
-953
@@ -1548,8 +1548,7 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/17254162?v=4",
|
||||
"profile": "https://www.linkedin.com/in/souzasamuel/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2547,7 +2546,7 @@
|
||||
"login": "tiennm99",
|
||||
"name": "Tien Nguyen Minh",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/39063457?v=4",
|
||||
"profile": "https://github.com/tiennm99",
|
||||
"profile": "http://miti99.dev",
|
||||
"contributions": [
|
||||
"code",
|
||||
"translation"
|
||||
@@ -2568,8 +2567,7 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/101238933?v=4",
|
||||
"profile": "https://surjendu-pal.netlify.app/",
|
||||
"contributions": [
|
||||
"translation",
|
||||
"code"
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2761,8 +2759,7 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/141746282?v=4",
|
||||
"profile": "https://github.com/jnniu-n",
|
||||
"contributions": [
|
||||
"translation",
|
||||
"doc"
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2835,9 +2832,7 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7249450?v=4",
|
||||
"profile": "https://github.com/ylcn91",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc",
|
||||
"review"
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2858,951 +2853,9 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "takikuba",
|
||||
"name": "Jakub Klimek",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/58841343?v=4",
|
||||
"profile": "https://github.com/takikuba",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bachbil",
|
||||
"name": "believe",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/28935461?v=4",
|
||||
"profile": "https://github.com/bachbil",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "egg0102030405",
|
||||
"name": "egg0102030405",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/59650695?v=4",
|
||||
"profile": "https://github.com/egg0102030405",
|
||||
"contributions": [
|
||||
"translation",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ved-asole",
|
||||
"name": "Ved Asole",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/77259804?v=4",
|
||||
"profile": "https://github.com/ved-asole",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Holiday-C",
|
||||
"name": "NewMorning",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/95869761?v=4",
|
||||
"profile": "https://github.com/Holiday-C",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Kiro-Young",
|
||||
"name": "资深老萌新",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/98251397?v=4",
|
||||
"profile": "https://github.com/Kiro-Young",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Nickolodeon98",
|
||||
"name": "Seunghwan Jeon",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/113348293?v=4",
|
||||
"profile": "https://itistori.tistory.com/",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sugan0tech",
|
||||
"name": "sugavanesh",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/71312159?v=4",
|
||||
"profile": "https://github.com/sugan0tech",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "FinnS-F",
|
||||
"name": "FinnS-F",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/126026830?v=4",
|
||||
"profile": "https://github.com/FinnS-F",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jerryyummy",
|
||||
"name": "jerryyummy",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/62087610?v=4",
|
||||
"profile": "https://github.com/jerryyummy",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Manojkumarchow",
|
||||
"name": "Manoj Chowdary",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/28005510?v=4",
|
||||
"profile": "https://manojkumarchow.github.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MaverickWingman",
|
||||
"name": "Aditya",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/76624290?v=4",
|
||||
"profile": "https://github.com/MaverickWingman",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nooynayr",
|
||||
"name": "nooynayr",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/141696269?v=4",
|
||||
"profile": "https://github.com/nooynayr",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pushpander1010",
|
||||
"name": "CYBERCRUX2",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/41006191?v=4",
|
||||
"profile": "https://github.com/pushpander1010",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "luismateoh",
|
||||
"name": "Luis Mateo Hincapié Martinez",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/44934142?v=4",
|
||||
"profile": "https://github.com/luismateoh",
|
||||
"contributions": [
|
||||
"translation",
|
||||
"review"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "guqing",
|
||||
"name": "guqing",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/38999863?v=4",
|
||||
"profile": "https://guqing.io",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sashirestela",
|
||||
"name": "Sashir Estela",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/14045506?v=4",
|
||||
"profile": "https://github.com/sashirestela",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "omahs",
|
||||
"name": "omahs",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/73983677?v=4",
|
||||
"profile": "https://github.com/omahs",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "leiferksn",
|
||||
"name": "leif e.",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4026636?v=4",
|
||||
"profile": "https://github.com/leiferksn",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "junhkang",
|
||||
"name": "Jun Kang",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/20232155?v=4",
|
||||
"profile": "https://junhkang.tistory.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "KishalayP",
|
||||
"name": "Kishalay Pandey",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/71012321?v=4",
|
||||
"profile": "https://github.com/KishalayP",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "drishtii7",
|
||||
"name": "drishtii7",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/82076566?v=4",
|
||||
"profile": "https://github.com/drishtii7",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "DavidMedinaO",
|
||||
"name": "David Medina Orozco",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/53974843?v=4",
|
||||
"profile": "https://github.com/DavidMedinaO",
|
||||
"contributions": [
|
||||
"translation",
|
||||
"review"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Romo4ka-bot",
|
||||
"name": "Roman Leontev",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/61774094?v=4",
|
||||
"profile": "https://github.com/Romo4ka-bot",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Ehspresso",
|
||||
"name": "Riley",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/144370752?v=4",
|
||||
"profile": "https://github.com/Ehspresso",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "k1w1dev",
|
||||
"name": "k1w1dev",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/121696782?v=4",
|
||||
"profile": "https://github.com/k1w1dev",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dev-yugantar",
|
||||
"name": "dev-yugantar",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/153066190?v=4",
|
||||
"profile": "https://github.com/dev-yugantar",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Adelechka",
|
||||
"name": "Adelya",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/65678470?v=4",
|
||||
"profile": "https://github.com/Adelechka",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gatlanagaprasanna",
|
||||
"name": "gatlanagaprasanna",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/154739216?v=4",
|
||||
"profile": "https://github.com/gatlanagaprasanna",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Avinash2110",
|
||||
"name": "Avinash Shukla",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/37360069?v=4",
|
||||
"profile": "https://github.com/Avinash2110",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Mayankchoudhary294",
|
||||
"name": "Mayank Choudhary",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/97609699?v=4",
|
||||
"profile": "https://github.com/Mayankchoudhary294",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "romannimets",
|
||||
"name": "romannimets",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/137268574?v=4",
|
||||
"profile": "https://github.com/romannimets",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Joel-Dandin",
|
||||
"name": "Joel",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/60149879?v=4",
|
||||
"profile": "https://github.com/Joel-Dandin",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "244Walyson",
|
||||
"name": "Walyson Moises",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/125759796?v=4",
|
||||
"profile": "https://github.com/244Walyson",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Xcyq",
|
||||
"name": "Xcyq",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/39086666?v=4",
|
||||
"profile": "https://github.com/Xcyq",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Ritabrata1080",
|
||||
"name": "Ritabrata",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/60834250?v=4",
|
||||
"profile": "https://github.com/Ritabrata1080",
|
||||
"contributions": [
|
||||
"review"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "trivikr",
|
||||
"name": "Trivikram Kamat",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/16024985?v=4",
|
||||
"profile": "https://github.com/trivikr",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vvanghelle",
|
||||
"name": "Vincent Vanghelle",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3204600?v=4",
|
||||
"profile": "https://github.com/vvanghelle",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "antoheri",
|
||||
"name": "Antoine Héritier",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/79988396?v=4",
|
||||
"profile": "https://github.com/antoheri",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fishandsheep",
|
||||
"name": "QinShower",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/43347407?v=4",
|
||||
"profile": "https://github.com/fishandsheep",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "LakshyaPunyani-01",
|
||||
"name": "LakshyaPunyani-01",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/103628913?v=4",
|
||||
"profile": "https://github.com/LakshyaPunyani-01",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jasonjyu",
|
||||
"name": "jasonjyu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10333076?v=4",
|
||||
"profile": "https://github.com/jasonjyu",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jeffmorrison",
|
||||
"name": "jeffmorrison",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/26047158?v=4",
|
||||
"profile": "https://github.com/jeffmorrison",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dmgodoy",
|
||||
"name": "David M.",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10840681?v=4",
|
||||
"profile": "https://github.com/dmgodoy",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "apophizzz",
|
||||
"name": "Patrick Kleindienst",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12052783?v=4",
|
||||
"profile": "https://github.com/apophizzz",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "proceane",
|
||||
"name": "Juyeon",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/62143949?v=4",
|
||||
"profile": "https://github.com/proceane",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mammadyahyayev",
|
||||
"name": "Mammad Yahyayev",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/66476643?v=4",
|
||||
"profile": "https://mammadyahya.vercel.app",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SalmaAzeem",
|
||||
"name": "Salma",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/121863224?v=4",
|
||||
"profile": "https://github.com/SalmaAzeem",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "CodeMaverick-143",
|
||||
"name": "Arpit Sarang",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/182847716?v=4",
|
||||
"profile": "https://codemaverick-143.github.io/My-Portfolio/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mayatarek",
|
||||
"name": "Maya",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/111644421?v=4",
|
||||
"profile": "https://github.com/mayatarek",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "HabibaMekay",
|
||||
"name": "HabibaMekay",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/133516736?v=4",
|
||||
"profile": "https://github.com/HabibaMekay",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Ahmed-Taha-981",
|
||||
"name": "Ahmed-Taha-981",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/122402269?v=4",
|
||||
"profile": "https://github.com/Ahmed-Taha-981",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "malak-elbanna",
|
||||
"name": "Malak Elbanna",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/67643605?v=4",
|
||||
"profile": "https://malakelbanna.netlify.app/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "depthlending",
|
||||
"name": "BiKangNing",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/164312726?v=4",
|
||||
"profile": "https://github.com/depthlending",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "TarunVishwakarma1",
|
||||
"name": "Tarun Vishwakarma",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/138651451?v=4",
|
||||
"profile": "https://github.com/TarunVishwakarma1",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "shahdhoss",
|
||||
"name": "Shahd Hossam",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/132148556?v=4",
|
||||
"profile": "https://github.com/shahdhoss",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mehdirahimi",
|
||||
"name": "Mehdi Rahimi",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/24210842?v=4",
|
||||
"profile": "https://mehdirahimi.github.io",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "clintaire",
|
||||
"name": "Clint Airé",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/111376518?v=4",
|
||||
"profile": "https://github.com/clintaire",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "darkhyper24",
|
||||
"name": "darkhyper24",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/132711528?v=4",
|
||||
"profile": "https://github.com/darkhyper24",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MohanedAtef238",
|
||||
"name": "Mohaned Atef",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/105852138?v=4",
|
||||
"profile": "https://github.com/MohanedAtef238",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "maximevtush",
|
||||
"name": "Maxim Evtush",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/154841002?v=4",
|
||||
"profile": "https://github.com/maximevtush",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hvgh88",
|
||||
"name": "Harshita Vidapanakal",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/65297242?v=4",
|
||||
"profile": "https://github.com/hvgh88",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "smile-ab",
|
||||
"name": "smile-ab",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/202159894?v=4",
|
||||
"profile": "https://github.com/smile-ab",
|
||||
"contributions": [
|
||||
"translation",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Francisco-G-P",
|
||||
"name": "Francisco-G-P",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/186766789?v=4",
|
||||
"profile": "https://github.com/Francisco-G-P",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Duartegdm",
|
||||
"name": "Gabriel Duarte",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/137895372?v=4",
|
||||
"profile": "https://github.com/Duartegdm",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "DenizAltunkapan",
|
||||
"name": "Deniz Altunkapan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/93663085?v=4",
|
||||
"profile": "https://github.com/DenizAltunkapan",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "johnklint81",
|
||||
"name": "John Klint",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/70539458?v=4",
|
||||
"profile": "https://github.com/johnklint81",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sanurah",
|
||||
"name": "Sanura Hettiarachchi",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/16178588?v=4",
|
||||
"profile": "https://github.com/sanurah",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "2897robo",
|
||||
"name": "Kim Gi Uk",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/31699375?v=4",
|
||||
"profile": "https://github.com/2897robo",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Suchismita-Deb",
|
||||
"name": "Suchismita Deb",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/68535074?v=4",
|
||||
"profile": "https://github.com/Suchismita-Deb",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ssrijan-007-sys",
|
||||
"name": "ssrijan-007-sys",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/137605821?v=4",
|
||||
"profile": "https://github.com/ssrijan-007-sys",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "e5LA",
|
||||
"name": "e5LA",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/208197507?v=4",
|
||||
"profile": "https://github.com/e5LA",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "maziyar-gerami",
|
||||
"name": "Maziyar Gerami",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/122622721?v=4",
|
||||
"profile": "http://maziyar-gerami.github.io/portfolio/",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "yybmion",
|
||||
"name": "yoobin_mion",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/113106136?v=4",
|
||||
"profile": "https://github.com/yybmion",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ronodhirSoumik",
|
||||
"name": "Soumik Sarker",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/46843689?v=4",
|
||||
"profile": "https://ronodhirsoumik.github.io",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "naman-sriv",
|
||||
"name": "Naman Srivastava",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/82610773?v=4",
|
||||
"profile": "https://github.com/naman-sriv",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "letdtcode",
|
||||
"name": "Thanh Nguyen Duc",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/92111552?v=4",
|
||||
"profile": "https://github.com/letdtcode",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "skamble2",
|
||||
"name": "Soham Kamble",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/121136639?v=4",
|
||||
"profile": "https://github.com/skamble2",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Olexandr88",
|
||||
"name": "Olexandr88",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/93856062?v=4",
|
||||
"profile": "https://github.com/Olexandr88",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nilsreichardt",
|
||||
"name": "Nils Reichardt",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/24459435?v=4",
|
||||
"profile": "https://github.com/nilsreichardt",
|
||||
"contributions": [
|
||||
"security"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "north-echo",
|
||||
"name": "Christopher Lusk",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/122107484?v=4",
|
||||
"profile": "http://www.northecho.dev",
|
||||
"contributions": [
|
||||
"security"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dagecko",
|
||||
"name": "dagecko",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6371704?v=4",
|
||||
"profile": "https://github.com/dagecko",
|
||||
"contributions": [
|
||||
"security"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jackmisbach",
|
||||
"name": "jackmisbach",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/183796460?v=4",
|
||||
"profile": "https://github.com/jackmisbach",
|
||||
"contributions": [
|
||||
"security"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "avivdon",
|
||||
"name": "avivdon",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/74790003?v=4",
|
||||
"profile": "https://github.com/avivdon",
|
||||
"contributions": [
|
||||
"security"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "quantdevv",
|
||||
"name": "Aditya Kumawat",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/56085648?v=4",
|
||||
"profile": "https://github.com/quantdevv",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "devch37",
|
||||
"name": "devch37",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/72903623?v=4",
|
||||
"profile": "https://github.com/devch37",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "HY-love-sleep",
|
||||
"name": "HY-love-sleep",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/73268470?v=4",
|
||||
"profile": "https://github.com/HY-love-sleep",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "CMD137",
|
||||
"name": "CMD137",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/56242956?v=4",
|
||||
"profile": "http://www.cmd137blog.top/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "QuantumRange",
|
||||
"name": "QuantumRange",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/49843948?v=4",
|
||||
"profile": "https://github.com/QuantumRange",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Dhiraj-birajdar",
|
||||
"name": "Dhiraj Birajdar",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/114354331?v=4",
|
||||
"profile": "https://github.com/Dhiraj-birajdar",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sieberss",
|
||||
"name": "Stefan Siebers",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/178474619?v=4",
|
||||
"profile": "https://github.com/sieberss",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "orbisai0security",
|
||||
"name": "OrbisAI Security",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/242526317?v=4",
|
||||
"profile": "https://orbisappsec.com/",
|
||||
"contributions": [
|
||||
"security"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rranjangupta",
|
||||
"name": "Raju Gupta",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/64011583?v=4",
|
||||
"profile": "https://www.rajugupta.com/portfolio/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "alex052525",
|
||||
"name": "김정모",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/116088682?v=4",
|
||||
"profile": "https://github.com/alex052525",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "AnakinYen0329",
|
||||
"name": "Anakin Yen",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/122139183?v=4",
|
||||
"profile": "https://github.com/AnakinYen0329",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SrGiuliano77",
|
||||
"name": "Giuliano Oliveira",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/128543464?v=4",
|
||||
"profile": "https://github.com/SrGiuliano77",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "priyanshuvishwakarma273403",
|
||||
"name": "Priyanshu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/222148451?v=4",
|
||||
"profile": "https://www.priyanshu-portfolio.co.in",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "arnabnandy7",
|
||||
"name": "Arnab Nandy",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5053648?v=4",
|
||||
"profile": "https://github.com/arnabnandy-cognizant",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MohamedMBG",
|
||||
"name": "BAGHDAD Mohamed",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/123114744?v=4",
|
||||
"profile": "https://github.com/MohamedMBG",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "AnveshSrivastava",
|
||||
"name": "Anvesh Srivastava",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/170334970?v=4",
|
||||
"profile": "https://github.com/AnveshSrivastava",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Mukul-Howale",
|
||||
"name": "Mukul Howale",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/110479646?v=4",
|
||||
"profile": "https://github.com/Mukul-Howale",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SandhyaDevadiga",
|
||||
"name": "Sandhya",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/128058717?v=4",
|
||||
"profile": "https://github.com/SandhyaDevadiga",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "devikae",
|
||||
"name": "dev-ikae",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/98100047?v=4",
|
||||
"profile": "https://github.com/devikae",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "zanarellidev",
|
||||
"name": "zanarelli",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/268068569?v=4",
|
||||
"profile": "https://github.com/zanarellidev",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 6,
|
||||
"contributorsPerLine": 7,
|
||||
"projectName": "java-design-patterns",
|
||||
"projectOwner": "iluwatar",
|
||||
"repoType": "github",
|
||||
|
||||
-366
@@ -1,366 +0,0 @@
|
||||
#
|
||||
# This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
#
|
||||
# The MIT License
|
||||
# Copyright © 2014-2022 Ilkka Seppälä
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = false
|
||||
max_line_length = off
|
||||
tab_width = 4
|
||||
ij_continuation_indent_size = 8
|
||||
ij_formatter_off_tag = @formatter:off
|
||||
ij_formatter_on_tag = @formatter:on
|
||||
ij_formatter_tags_enabled = true
|
||||
ij_smart_tabs = false
|
||||
ij_visual_guides =
|
||||
ij_wrap_on_typing = false
|
||||
|
||||
[*.java]
|
||||
indent_size = 2
|
||||
max_line_length = 100
|
||||
ij_continuation_indent_size = 4
|
||||
ij_java_align_consecutive_assignments = false
|
||||
ij_java_align_consecutive_variable_declarations = false
|
||||
ij_java_align_group_field_declarations = false
|
||||
ij_java_align_multiline_annotation_parameters = false
|
||||
ij_java_align_multiline_array_initializer_expression = false
|
||||
ij_java_align_multiline_assignment = false
|
||||
ij_java_align_multiline_binary_operation = false
|
||||
ij_java_align_multiline_chained_methods = false
|
||||
ij_java_align_multiline_deconstruction_list_components = true
|
||||
ij_java_align_multiline_extends_list = false
|
||||
ij_java_align_multiline_for = true
|
||||
ij_java_align_multiline_method_parentheses = false
|
||||
ij_java_align_multiline_parameters = true
|
||||
ij_java_align_multiline_parameters_in_calls = false
|
||||
ij_java_align_multiline_parenthesized_expression = false
|
||||
ij_java_align_multiline_records = true
|
||||
ij_java_align_multiline_resources = true
|
||||
ij_java_align_multiline_ternary_operation = false
|
||||
ij_java_align_multiline_text_blocks = false
|
||||
ij_java_align_multiline_throws_list = false
|
||||
ij_java_align_subsequent_simple_methods = false
|
||||
ij_java_align_throws_keyword = false
|
||||
ij_java_align_types_in_multi_catch = true
|
||||
ij_java_annotation_parameter_wrap = off
|
||||
ij_java_array_initializer_new_line_after_left_brace = false
|
||||
ij_java_array_initializer_right_brace_on_new_line = false
|
||||
ij_java_array_initializer_wrap = normal
|
||||
ij_java_assert_statement_colon_on_next_line = false
|
||||
ij_java_assert_statement_wrap = normal
|
||||
ij_java_assignment_wrap = normal
|
||||
ij_java_binary_operation_sign_on_next_line = false
|
||||
ij_java_binary_operation_wrap = normal
|
||||
ij_java_blank_lines_after_anonymous_class_header = 0
|
||||
ij_java_blank_lines_after_class_header = 0
|
||||
ij_java_blank_lines_after_imports = 1
|
||||
ij_java_blank_lines_after_package = 1
|
||||
ij_java_blank_lines_around_class = 1
|
||||
ij_java_blank_lines_around_field = 0
|
||||
ij_java_blank_lines_around_field_in_interface = 0
|
||||
ij_java_blank_lines_around_initializer = 1
|
||||
ij_java_blank_lines_around_method = 1
|
||||
ij_java_blank_lines_around_method_in_interface = 1
|
||||
ij_java_blank_lines_before_class_end = 0
|
||||
ij_java_blank_lines_before_imports = 1
|
||||
ij_java_blank_lines_before_method_body = 0
|
||||
ij_java_blank_lines_before_package = 1
|
||||
ij_java_block_brace_style = end_of_line
|
||||
ij_java_block_comment_add_space = false
|
||||
ij_java_block_comment_at_first_column = true
|
||||
ij_java_builder_methods =
|
||||
ij_java_call_parameters_new_line_after_left_paren = false
|
||||
ij_java_call_parameters_right_paren_on_new_line = false
|
||||
ij_java_call_parameters_wrap = normal
|
||||
ij_java_case_statement_on_separate_line = true
|
||||
ij_java_catch_on_new_line = false
|
||||
ij_java_class_annotation_wrap = split_into_lines
|
||||
ij_java_class_brace_style = end_of_line
|
||||
ij_java_class_count_to_use_import_on_demand = 999
|
||||
ij_java_class_names_in_javadoc = 1
|
||||
ij_java_deconstruction_list_wrap = normal
|
||||
ij_java_do_not_indent_top_level_class_members = false
|
||||
ij_java_do_not_wrap_after_single_annotation = false
|
||||
ij_java_do_not_wrap_after_single_annotation_in_parameter = false
|
||||
ij_java_do_while_brace_force = always
|
||||
ij_java_doc_add_blank_line_after_description = true
|
||||
ij_java_doc_add_blank_line_after_param_comments = false
|
||||
ij_java_doc_add_blank_line_after_return = false
|
||||
ij_java_doc_add_p_tag_on_empty_lines = true
|
||||
ij_java_doc_align_exception_comments = true
|
||||
ij_java_doc_align_param_comments = true
|
||||
ij_java_doc_do_not_wrap_if_one_line = false
|
||||
ij_java_doc_enable_formatting = true
|
||||
ij_java_doc_enable_leading_asterisks = true
|
||||
ij_java_doc_indent_on_continuation = false
|
||||
ij_java_doc_keep_empty_lines = true
|
||||
ij_java_doc_keep_empty_parameter_tag = true
|
||||
ij_java_doc_keep_empty_return_tag = true
|
||||
ij_java_doc_keep_empty_throws_tag = true
|
||||
ij_java_doc_keep_invalid_tags = true
|
||||
ij_java_doc_param_description_on_new_line = false
|
||||
ij_java_doc_preserve_line_breaks = false
|
||||
ij_java_doc_use_throws_not_exception_tag = true
|
||||
ij_java_else_on_new_line = false
|
||||
ij_java_entity_dd_prefix =
|
||||
ij_java_entity_dd_suffix = EJB
|
||||
ij_java_entity_eb_prefix =
|
||||
ij_java_entity_eb_suffix = Bean
|
||||
ij_java_entity_hi_prefix =
|
||||
ij_java_entity_hi_suffix = Home
|
||||
ij_java_entity_lhi_prefix = Local
|
||||
ij_java_entity_lhi_suffix = Home
|
||||
ij_java_entity_li_prefix = Local
|
||||
ij_java_entity_li_suffix =
|
||||
ij_java_entity_pk_class = java.lang.String
|
||||
ij_java_entity_ri_prefix =
|
||||
ij_java_entity_ri_suffix =
|
||||
ij_java_entity_vo_prefix =
|
||||
ij_java_entity_vo_suffix = VO
|
||||
ij_java_enum_constants_wrap = normal
|
||||
ij_java_extends_keyword_wrap = normal
|
||||
ij_java_extends_list_wrap = normal
|
||||
ij_java_field_annotation_wrap = split_into_lines
|
||||
ij_java_field_name_prefix =
|
||||
ij_java_field_name_suffix =
|
||||
ij_java_filter_class_prefix =
|
||||
ij_java_filter_class_suffix =
|
||||
ij_java_filter_dd_prefix =
|
||||
ij_java_filter_dd_suffix =
|
||||
ij_java_finally_on_new_line = false
|
||||
ij_java_for_brace_force = always
|
||||
ij_java_for_statement_new_line_after_left_paren = false
|
||||
ij_java_for_statement_right_paren_on_new_line = false
|
||||
ij_java_for_statement_wrap = normal
|
||||
ij_java_generate_final_locals = false
|
||||
ij_java_generate_final_parameters = false
|
||||
ij_java_if_brace_force = always
|
||||
ij_java_imports_layout = $*,|,*
|
||||
ij_java_indent_case_from_switch = true
|
||||
ij_java_insert_inner_class_imports = false
|
||||
ij_java_insert_override_annotation = true
|
||||
ij_java_keep_blank_lines_before_right_brace = 2
|
||||
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
|
||||
ij_java_keep_blank_lines_in_code = 2
|
||||
ij_java_keep_blank_lines_in_declarations = 2
|
||||
ij_java_keep_builder_methods_indents = false
|
||||
ij_java_keep_control_statement_in_one_line = true
|
||||
ij_java_keep_first_column_comment = true
|
||||
ij_java_keep_indents_on_empty_lines = false
|
||||
ij_java_keep_line_breaks = true
|
||||
ij_java_keep_multiple_expressions_in_one_line = false
|
||||
ij_java_keep_simple_blocks_in_one_line = false
|
||||
ij_java_keep_simple_classes_in_one_line = false
|
||||
ij_java_keep_simple_lambdas_in_one_line = false
|
||||
ij_java_keep_simple_methods_in_one_line = false
|
||||
ij_java_label_indent_absolute = false
|
||||
ij_java_label_indent_size = 0
|
||||
ij_java_lambda_brace_style = end_of_line
|
||||
ij_java_layout_static_imports_separately = true
|
||||
ij_java_line_comment_add_space = false
|
||||
ij_java_line_comment_add_space_on_reformat = false
|
||||
ij_java_line_comment_at_first_column = true
|
||||
ij_java_listener_class_prefix =
|
||||
ij_java_listener_class_suffix =
|
||||
ij_java_local_variable_name_prefix =
|
||||
ij_java_local_variable_name_suffix =
|
||||
ij_java_message_dd_prefix =
|
||||
ij_java_message_dd_suffix = EJB
|
||||
ij_java_message_eb_prefix =
|
||||
ij_java_message_eb_suffix = Bean
|
||||
ij_java_method_annotation_wrap = split_into_lines
|
||||
ij_java_method_brace_style = end_of_line
|
||||
ij_java_method_call_chain_wrap = normal
|
||||
ij_java_method_parameters_new_line_after_left_paren = false
|
||||
ij_java_method_parameters_right_paren_on_new_line = false
|
||||
ij_java_method_parameters_wrap = normal
|
||||
ij_java_modifier_list_wrap = false
|
||||
ij_java_multi_catch_types_wrap = normal
|
||||
ij_java_names_count_to_use_import_on_demand = 999
|
||||
ij_java_new_line_after_lparen_in_annotation = false
|
||||
ij_java_new_line_after_lparen_in_deconstruction_pattern = true
|
||||
ij_java_new_line_after_lparen_in_record_header = false
|
||||
ij_java_packages_to_use_import_on_demand =
|
||||
ij_java_parameter_annotation_wrap = normal
|
||||
ij_java_parameter_name_prefix =
|
||||
ij_java_parameter_name_suffix =
|
||||
ij_java_parentheses_expression_new_line_after_left_paren = false
|
||||
ij_java_parentheses_expression_right_paren_on_new_line = false
|
||||
ij_java_place_assignment_sign_on_next_line = false
|
||||
ij_java_prefer_longer_names = true
|
||||
ij_java_prefer_parameters_wrap = false
|
||||
ij_java_record_components_wrap = normal
|
||||
ij_java_repeat_annotations =
|
||||
ij_java_repeat_synchronized = true
|
||||
ij_java_replace_instanceof_and_cast = false
|
||||
ij_java_replace_null_check = true
|
||||
ij_java_replace_sum_lambda_with_method_ref = true
|
||||
ij_java_resource_list_new_line_after_left_paren = false
|
||||
ij_java_resource_list_right_paren_on_new_line = false
|
||||
ij_java_resource_list_wrap = normal
|
||||
ij_java_rparen_on_new_line_in_annotation = false
|
||||
ij_java_rparen_on_new_line_in_deconstruction_pattern = true
|
||||
ij_java_rparen_on_new_line_in_record_header = false
|
||||
ij_java_servlet_class_prefix =
|
||||
ij_java_servlet_class_suffix =
|
||||
ij_java_servlet_dd_prefix =
|
||||
ij_java_servlet_dd_suffix =
|
||||
ij_java_session_dd_prefix =
|
||||
ij_java_session_dd_suffix = EJB
|
||||
ij_java_session_eb_prefix =
|
||||
ij_java_session_eb_suffix = Bean
|
||||
ij_java_session_hi_prefix =
|
||||
ij_java_session_hi_suffix = Home
|
||||
ij_java_session_lhi_prefix = Local
|
||||
ij_java_session_lhi_suffix = Home
|
||||
ij_java_session_li_prefix = Local
|
||||
ij_java_session_li_suffix =
|
||||
ij_java_session_ri_prefix =
|
||||
ij_java_session_ri_suffix =
|
||||
ij_java_session_si_prefix =
|
||||
ij_java_session_si_suffix = Service
|
||||
ij_java_space_after_closing_angle_bracket_in_type_argument = false
|
||||
ij_java_space_after_colon = true
|
||||
ij_java_space_after_comma = true
|
||||
ij_java_space_after_comma_in_type_arguments = true
|
||||
ij_java_space_after_for_semicolon = true
|
||||
ij_java_space_after_quest = true
|
||||
ij_java_space_after_type_cast = true
|
||||
ij_java_space_before_annotation_array_initializer_left_brace = false
|
||||
ij_java_space_before_annotation_parameter_list = false
|
||||
ij_java_space_before_array_initializer_left_brace = true
|
||||
ij_java_space_before_catch_keyword = true
|
||||
ij_java_space_before_catch_left_brace = true
|
||||
ij_java_space_before_catch_parentheses = true
|
||||
ij_java_space_before_class_left_brace = true
|
||||
ij_java_space_before_colon = true
|
||||
ij_java_space_before_colon_in_foreach = true
|
||||
ij_java_space_before_comma = false
|
||||
ij_java_space_before_deconstruction_list = false
|
||||
ij_java_space_before_do_left_brace = true
|
||||
ij_java_space_before_else_keyword = true
|
||||
ij_java_space_before_else_left_brace = true
|
||||
ij_java_space_before_finally_keyword = true
|
||||
ij_java_space_before_finally_left_brace = true
|
||||
ij_java_space_before_for_left_brace = true
|
||||
ij_java_space_before_for_parentheses = true
|
||||
ij_java_space_before_for_semicolon = false
|
||||
ij_java_space_before_if_left_brace = true
|
||||
ij_java_space_before_if_parentheses = true
|
||||
ij_java_space_before_method_call_parentheses = false
|
||||
ij_java_space_before_method_left_brace = true
|
||||
ij_java_space_before_method_parentheses = false
|
||||
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
|
||||
ij_java_space_before_quest = true
|
||||
ij_java_space_before_switch_left_brace = true
|
||||
ij_java_space_before_switch_parentheses = true
|
||||
ij_java_space_before_synchronized_left_brace = true
|
||||
ij_java_space_before_synchronized_parentheses = true
|
||||
ij_java_space_before_try_left_brace = true
|
||||
ij_java_space_before_try_parentheses = true
|
||||
ij_java_space_before_type_parameter_list = false
|
||||
ij_java_space_before_while_keyword = true
|
||||
ij_java_space_before_while_left_brace = true
|
||||
ij_java_space_before_while_parentheses = true
|
||||
ij_java_space_inside_one_line_enum_braces = false
|
||||
ij_java_space_within_empty_array_initializer_braces = false
|
||||
ij_java_space_within_empty_method_call_parentheses = false
|
||||
ij_java_space_within_empty_method_parentheses = false
|
||||
ij_java_spaces_around_additive_operators = true
|
||||
ij_java_spaces_around_annotation_eq = true
|
||||
ij_java_spaces_around_assignment_operators = true
|
||||
ij_java_spaces_around_bitwise_operators = true
|
||||
ij_java_spaces_around_equality_operators = true
|
||||
ij_java_spaces_around_lambda_arrow = true
|
||||
ij_java_spaces_around_logical_operators = true
|
||||
ij_java_spaces_around_method_ref_dbl_colon = false
|
||||
ij_java_spaces_around_multiplicative_operators = true
|
||||
ij_java_spaces_around_relational_operators = true
|
||||
ij_java_spaces_around_shift_operators = true
|
||||
ij_java_spaces_around_type_bounds_in_type_parameters = true
|
||||
ij_java_spaces_around_unary_operator = false
|
||||
ij_java_spaces_within_angle_brackets = false
|
||||
ij_java_spaces_within_annotation_parentheses = false
|
||||
ij_java_spaces_within_array_initializer_braces = false
|
||||
ij_java_spaces_within_braces = false
|
||||
ij_java_spaces_within_brackets = false
|
||||
ij_java_spaces_within_cast_parentheses = false
|
||||
ij_java_spaces_within_catch_parentheses = false
|
||||
ij_java_spaces_within_deconstruction_list = false
|
||||
ij_java_spaces_within_for_parentheses = false
|
||||
ij_java_spaces_within_if_parentheses = false
|
||||
ij_java_spaces_within_method_call_parentheses = false
|
||||
ij_java_spaces_within_method_parentheses = false
|
||||
ij_java_spaces_within_parentheses = false
|
||||
ij_java_spaces_within_record_header = false
|
||||
ij_java_spaces_within_switch_parentheses = false
|
||||
ij_java_spaces_within_synchronized_parentheses = false
|
||||
ij_java_spaces_within_try_parentheses = false
|
||||
ij_java_spaces_within_while_parentheses = false
|
||||
ij_java_special_else_if_treatment = true
|
||||
ij_java_static_field_name_prefix =
|
||||
ij_java_static_field_name_suffix =
|
||||
ij_java_subclass_name_prefix =
|
||||
ij_java_subclass_name_suffix = Impl
|
||||
ij_java_ternary_operation_signs_on_next_line = false
|
||||
ij_java_ternary_operation_wrap = normal
|
||||
ij_java_test_name_prefix =
|
||||
ij_java_test_name_suffix = Test
|
||||
ij_java_throws_keyword_wrap = normal
|
||||
ij_java_throws_list_wrap = normal
|
||||
ij_java_use_external_annotations = false
|
||||
ij_java_use_fq_class_names = false
|
||||
ij_java_use_relative_indents = false
|
||||
ij_java_use_single_class_imports = true
|
||||
ij_java_variable_annotation_wrap = normal
|
||||
ij_java_visibility = public
|
||||
ij_java_while_brace_force = always
|
||||
ij_java_while_on_new_line = false
|
||||
ij_java_wrap_comments = false
|
||||
ij_java_wrap_first_method_in_call_chain = false
|
||||
ij_java_wrap_long_lines = false
|
||||
|
||||
[{*.markdown,*.md}]
|
||||
ij_markdown_force_one_space_after_blockquote_symbol = true
|
||||
ij_markdown_force_one_space_after_header_symbol = true
|
||||
ij_markdown_force_one_space_after_list_bullet = true
|
||||
ij_markdown_force_one_space_between_words = true
|
||||
ij_markdown_format_tables = true
|
||||
ij_markdown_insert_quote_arrows_on_wrap = true
|
||||
ij_markdown_keep_indents_on_empty_lines = false
|
||||
ij_markdown_keep_line_breaks_inside_text_blocks = true
|
||||
ij_markdown_max_line_length = off
|
||||
ij_markdown_max_lines_around_block_elements = 1
|
||||
ij_markdown_max_lines_around_header = 1
|
||||
ij_markdown_max_lines_between_paragraphs = 1
|
||||
ij_markdown_min_lines_around_block_elements = 1
|
||||
ij_markdown_min_lines_around_header = 1
|
||||
ij_markdown_min_lines_between_paragraphs = 1
|
||||
ij_markdown_wrap_text_if_long = true
|
||||
ij_markdown_wrap_text_inside_blockquotes = true
|
||||
+15
-1
@@ -8,4 +8,18 @@ updates:
|
||||
- package-ecosystem: "maven" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
interval: "weekly"
|
||||
allow:
|
||||
- dependency-name: "org.springframework.boot:spring-boot-dependencies"
|
||||
- dependency-name: "commons-dbcp:commons-dbcp"
|
||||
- dependency-name: "net.sourceforge.htmlunit:htmlunit"
|
||||
- dependency-name: "com.google.code.gson:gson"
|
||||
- dependency-name: "com.google.inject:guice"
|
||||
- dependency-name: "com.github.stefanbirkner:system-lambda"
|
||||
- dependency-name: "org.apache.maven.plugins:maven-surefire-plugin"
|
||||
- dependency-name: "org.apache.maven.plugins:maven-assembly-plugin"
|
||||
- dependency-name: "org.sonarsource.scanner.maven:sonar-maven-plugin"
|
||||
- dependency-name: "org.apache.maven.plugins:maven-checkstyle-plugin"
|
||||
- dependency-name: "com.mycila:license-maven-plugin"
|
||||
- dependency-name: "org.jacoco:jacoco-maven-plugin"
|
||||
- dependency-name: "com.iluwatar.urm:urm-maven-plugin"
|
||||
@@ -0,0 +1,61 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 60
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: false
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
||||
onlyLabels: []
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
exemptLabels:
|
||||
- "info: help wanted"
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: false
|
||||
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: false
|
||||
|
||||
# Label to use when marking as stale
|
||||
staleLabel: "status: stale"
|
||||
|
||||
# Comment to post when marking as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. The issue will be unassigned if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
|
||||
# Comment to post when removing the stale label.
|
||||
# unmarkComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Comment to post when closing a stale Issue or Pull Request.
|
||||
# closeComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 30
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
# only: issues
|
||||
|
||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
||||
pulls:
|
||||
daysUntilStale: 30
|
||||
daysUntilClose: 45
|
||||
markComment: >
|
||||
This pull request has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs.
|
||||
closeComment: >
|
||||
Closed due to inactivity. Thank you for your contributions.
|
||||
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - confirmed
|
||||
@@ -1,3 +1,32 @@
|
||||
#
|
||||
# The MIT License
|
||||
# Copyright © 2014-2021 Ilkka Seppälä
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
|
||||
# This workflow will build a Java project with Maven
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
# We are using two jobs here for testing our code on the latest JDK 11 build as well as a more satble build version of 11.0.3
|
||||
# You can see the full discussion here https://github.com/iluwatar/java-design-patterns/pull/1868#issue-1029459688
|
||||
|
||||
name: Java CI
|
||||
|
||||
on:
|
||||
@@ -8,51 +37,46 @@ jobs:
|
||||
|
||||
build-and-analyze:
|
||||
|
||||
name: Build and Run Sonar analysis on JDK 21
|
||||
runs-on: ubuntu-22.04
|
||||
name: Build and Run Sonar analysis on JDK 17
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# Disabling shallow clone for improving relevancy of SonarQube reporting
|
||||
fetch-depth: 0
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Disabling shallow clone for improving relevancy of SonarQube reporting
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
cache: 'maven'
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
cache: 'maven'
|
||||
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
# Cache Sonar packages which are used to run analysis and collect metrics
|
||||
- name: Cache SonarCloud packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
restore-keys: ${{ runner.os }}-sonar
|
||||
# Cache Sonar packages which as used to run analysis and collect metrics
|
||||
- name: Cache SonarCloud packages
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
restore-keys: ${{ runner.os }}-sonar
|
||||
|
||||
# Some tests need screen access
|
||||
- name: Install xvfb
|
||||
run: sudo apt-get install -y xvfb
|
||||
# Some tests need screen access
|
||||
- name: Install xvfb
|
||||
run: sudo apt-get install -y xvfb
|
||||
|
||||
- name: Build with Maven and run SonarQube analysis
|
||||
run: xvfb-run ./mvnw clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
||||
env:
|
||||
# These two env variables are needed for sonar analysis
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Build with Maven and run SonarQube analysis
|
||||
run: xvfb-run ./mvnw clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
||||
env:
|
||||
# These two env variables are needed for sonar analysis
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
@@ -1,7 +1,33 @@
|
||||
#
|
||||
# The MIT License
|
||||
# Copyright © 2014-2021 Ilkka Seppälä
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
|
||||
# This workflow will build a Java project with Maven
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
name: Java PR Builder
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
branches: [ master ]
|
||||
types: [ opened, reopened, synchronize ]
|
||||
|
||||
@@ -11,36 +37,47 @@ permissions:
|
||||
jobs:
|
||||
build-and-analyze:
|
||||
|
||||
name: Build on JDK 21
|
||||
runs-on: ubuntu-22.04
|
||||
name: Build on JDK 17
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
cache: 'maven'
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
cache: 'maven'
|
||||
# Cache Sonar packages which as used to run analysis and collect metrics
|
||||
- name: Cache SonarCloud packages
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
restore-keys: ${{ runner.os }}-sonar
|
||||
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
# Some tests need screen access
|
||||
- name: Install xvfb
|
||||
run: sudo apt-get install -y xvfb
|
||||
|
||||
# Some tests need screen access
|
||||
- name: Install xvfb
|
||||
run: sudo apt-get install -y xvfb
|
||||
|
||||
- name: Build with Maven
|
||||
run: xvfb-run ./mvnw clean verify
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Build with Maven and run SonarQube analysis
|
||||
env:
|
||||
# Intermediate variable
|
||||
HEAD_REF: ${{ github.head_ref }}
|
||||
# These two env variables are needed for sonar analysis
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
run: xvfb-run ./mvnw clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=iluwatar -Dsonar.projectKey=iluwatar_java-design-patterns -Dsonar.pullrequest.branch=$HEAD_REF -Dsonar.pullrequest.base=${{ github.base_ref }} -Dsonar.pullrequest.key=${{ github.event.pull_request.number }}
|
||||
@@ -1,28 +0,0 @@
|
||||
name: Presubmit.ai
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check required secrets
|
||||
run: |
|
||||
if [ -z "${{ secrets.LLM_API_KEY }}" ]; then
|
||||
echo "Error: LLM_API_KEY secret is not configured"
|
||||
exit 1
|
||||
fi
|
||||
- uses: presubmit/ai-reviewer@latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_MODEL: "gpt-5-nano"
|
||||
@@ -1,20 +0,0 @@
|
||||
name: 'Comment on stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
stale-issue-message: 'This issue is stale because it has been open 60 days with no activity.'
|
||||
stale-pr-message: 'This PR is stale because it has been open 60 days with no activity.'
|
||||
close-issue-message: 'This issue was closed because it has been stalled for too long with no activity.'
|
||||
close-pr-message: 'This PR was closed because it has been stalled for too long with no activity.'
|
||||
days-before-issue-stale: 60
|
||||
days-before-pr-stale: 60
|
||||
days-before-issue-close: -1
|
||||
days-before-pr-close: -1
|
||||
exempt-issue-labels: 'info: help wanted'
|
||||
@@ -57,5 +57,3 @@ build/
|
||||
#################### Java Design Patterns #######
|
||||
etc/Java Design Patterns.urm.puml
|
||||
serialized-entity/output.txt
|
||||
fish1.out
|
||||
fish2.out
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2021 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MavenWrapperDownloader {
|
||||
|
||||
private static final String WRAPPER_VERSION = "0.5.6";
|
||||
/**
|
||||
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
|
||||
*/
|
||||
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
|
||||
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
|
||||
|
||||
/**
|
||||
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
|
||||
* use instead of the default one.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
|
||||
".mvn/wrapper/maven-wrapper.properties";
|
||||
|
||||
/**
|
||||
* Path where the maven-wrapper.jar will be saved to.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_JAR_PATH =
|
||||
".mvn/wrapper/maven-wrapper.jar";
|
||||
|
||||
/**
|
||||
* Name of the property which should be used to override the default download url for the wrapper.
|
||||
*/
|
||||
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("- Downloader started");
|
||||
File baseDirectory = new File(args[0]);
|
||||
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
|
||||
|
||||
// If the maven-wrapper.properties exists, read it and check if it contains a custom
|
||||
// wrapperUrl parameter.
|
||||
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
|
||||
String url = DEFAULT_DOWNLOAD_URL;
|
||||
if(mavenWrapperPropertyFile.exists()) {
|
||||
FileInputStream mavenWrapperPropertyFileInputStream = null;
|
||||
try {
|
||||
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
|
||||
Properties mavenWrapperProperties = new Properties();
|
||||
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
|
||||
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
|
||||
} catch (IOException e) {
|
||||
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
|
||||
} finally {
|
||||
try {
|
||||
if(mavenWrapperPropertyFileInputStream != null) {
|
||||
mavenWrapperPropertyFileInputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore ...
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading from: " + url);
|
||||
|
||||
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
|
||||
if(!outputFile.getParentFile().exists()) {
|
||||
if(!outputFile.getParentFile().mkdirs()) {
|
||||
System.out.println(
|
||||
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
|
||||
try {
|
||||
downloadFileFromURL(url, outputFile);
|
||||
System.out.println("Done");
|
||||
System.exit(0);
|
||||
} catch (Throwable e) {
|
||||
System.out.println("- Error downloading");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
|
||||
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
|
||||
String username = System.getenv("MVNW_USERNAME");
|
||||
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
|
||||
Authenticator.setDefault(new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(username, password);
|
||||
}
|
||||
});
|
||||
}
|
||||
URL website = new URL(urlString);
|
||||
ReadableByteChannel rbc;
|
||||
rbc = Channels.newChannel(website.openStream());
|
||||
FileOutputStream fos = new FileOutputStream(destination);
|
||||
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
||||
fos.close();
|
||||
rbc.close();
|
||||
}
|
||||
|
||||
}
|
||||
+5
-5
@@ -5,14 +5,14 @@
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
|
||||
@@ -1,51 +0,0 @@
|
||||
# Java Design Patterns - Priming Context for AI Agents
|
||||
|
||||
## Quick Overview
|
||||
- This repository is a comprehensive collection of design patterns implemented in Java.
|
||||
- The project demonstrates how to solve common software design problems using standard patterns.
|
||||
- The code for each pattern lives directly in this repository, alongside its explanatory README.
|
||||
- These readmes are published on the java-design-patterns.com website.
|
||||
- Another repository (https://github.com/iluwatar/java-design-patterns-vuepress-web) handles the deployment of the website.
|
||||
|
||||
## Stack
|
||||
- **Java 21**: The primary programming language used for pattern implementations.
|
||||
- **Maven**: Dependency management and build tool.
|
||||
- **JUnit 5**: The testing framework used to verify pattern behaviors.
|
||||
- **Mockito**: Used for mocking dependencies in unit tests.
|
||||
- **Lombok**: Used to reduce boilerplate code (getters, setters, etc.).
|
||||
- **Spotless**: Enforces consistent code formatting via Google Java Format.
|
||||
|
||||
## Trusted Sources
|
||||
- [Java SE 21 Documentation](https://docs.oracle.com/en/java/javase/21/docs/api/)
|
||||
- [Maven Official Documentation](https://maven.apache.org/guides/index.html)
|
||||
- [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide/)
|
||||
- [Project Wiki](https://github.com/iluwatar/java-design-patterns/wiki)
|
||||
|
||||
## Structure
|
||||
- `/pom.xml`: The root Maven configuration file that defines global dependencies and lists all pattern modules.
|
||||
- `/[pattern-name]/`: Individual folders for each design pattern (e.g., `/abstract-factory`, `/builder`), acting as standalone Maven modules.
|
||||
- `/[pattern-name]/src/main/java/`: Contains the actual Java implementation classes of the specific design pattern.
|
||||
- `/[pattern-name]/src/test/java/`: Contains the JUnit tests verifying the pattern's behavior.
|
||||
- `/[pattern-name]/README.md`: The documentation for the pattern, which gets published to the main website.
|
||||
|
||||
## Patterns
|
||||
- Keep pattern implementations simple, atomic, and easy to understand.
|
||||
- Write descriptive and meaningful names for classes, interfaces, and methods.
|
||||
- Always include comprehensive unit tests for every new pattern or code modification.
|
||||
- Follow the Google Java Format strictly (enforced by Spotless).
|
||||
- Document the intent, explanation, and real-world usage clearly in each module's `README.md`.
|
||||
|
||||
## Anti-patterns
|
||||
- Avoid overcomplicating patterns with unnecessary external dependencies or complex frameworks.
|
||||
- Do not introduce business logic that distracts from the core mechanism of the design pattern itself.
|
||||
- Submitting new patterns or features without corresponding unit tests is strictly discouraged.
|
||||
- Avoid large monolithic packages; each pattern should reside in its own isolated module.
|
||||
|
||||
## Example Design Pattern
|
||||
When a new design pattern is added to the repository, it generally follows these steps:
|
||||
- **Create a Module**: Create a new folder for the pattern in the root directory (e.g., `/my-new-pattern`).
|
||||
- **Update root pom.xml**: Add `<module>my-new-pattern</module>` to the `<modules>` section of the root `pom.xml`.
|
||||
- **Add Module pom.xml**: Create a `pom.xml` inside the new folder that inherits from the parent project.
|
||||
- **Write the Code**: Implement the pattern logic under `src/main/java/com/iluwatar/mynewpattern`, usually including an `App.java` class to demonstrate its usage.
|
||||
- **Write the Tests**: Add comprehensive unit tests under `src/test/java/com/iluwatar/mynewpattern`.
|
||||
- **Document**: Create a `README.md` at the root of the new module, structured with standard sections like Intent, Explanation, Class diagram, Applicability, and Real world examples.
|
||||
+3
-2
@@ -1,3 +1,4 @@
|
||||
The project guidelines can be found in [Java Design Patterns developer wiki](https://github.com/iluwatar/java-design-patterns/wiki).
|
||||
This is great you have something to contribute!
|
||||
|
||||
A good place to start is: https://github.com/iluwatar/java-design-patterns/wiki/01.-How-to-contribute
|
||||
Before going any further please read the [wiki](https://github.com/iluwatar/java-design-patterns/wiki)
|
||||
with conventions and rules we used for this project.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright � 2014-2024 Ilkka Seppälä
|
||||
Copyright © 2014-2021 Ilkka Seppälä
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
# Pull Request Template
|
||||
|
||||
## What does this PR do?
|
||||
Pull request title
|
||||
|
||||
<!-- Provide a short description of what this pull request does. -->
|
||||
- Clearly and concisely describes what it does
|
||||
- Refer to the issue that it solves, if available
|
||||
|
||||
<!-- Fixes #<issue-number> (if applicable) -->
|
||||
|
||||
Pull request description
|
||||
|
||||
- Describes the main changes that come with the pull request
|
||||
- Any relevant additional information is provided
|
||||
|
||||
|
||||
|
||||
> For detailed contributing instructions see https://github.com/iluwatar/java-design-patterns/wiki/01.-How-to-contribute
|
||||
+100
-141
@@ -1,29 +1,25 @@
|
||||
---
|
||||
title: "Abstract Document Pattern in Java: Simplifying Data Handling with Flexibility"
|
||||
shortTitle: Abstract Document
|
||||
description: "Explore the Abstract Document design pattern in Java. Learn its intent, explanation, applicability, benefits, and see real-world examples to implement flexible and dynamic data structures."
|
||||
title: Abstract Document
|
||||
category: Structural
|
||||
language: en
|
||||
tag:
|
||||
- Abstraction
|
||||
- Decoupling
|
||||
- Dynamic typing
|
||||
- Encapsulation
|
||||
- Extensibility
|
||||
- Polymorphism
|
||||
tag:
|
||||
- Extensibility
|
||||
---
|
||||
|
||||
## Intent of Abstract Document Design Pattern
|
||||
## Intent
|
||||
|
||||
The Abstract Document design pattern in Java is a crucial structural design pattern that provides a consistent way to handle hierarchical and tree-like data structures by defining a common interface for various document types. It separates the core document structure from specific data formats, enabling dynamic updates and simplified maintenance.
|
||||
The "Abstract Document" design pattern is a structural design pattern that aims to provide a consistent way to handle
|
||||
hierarchical and tree-like data structures by defining a common interface for various document types.
|
||||
|
||||
## Detailed Explanation of Abstract Document Pattern with Real-World Examples
|
||||
## Explanation
|
||||
|
||||
The Abstract Document design pattern in Java allows dynamic handling of non-static properties. This pattern uses concept of traits to enable type safety and separate properties of different classes into set of interfaces.
|
||||
The Abstract Document pattern enables handling additional, non-static properties. This pattern
|
||||
uses concept of traits to enable type safety and separate properties of different classes into
|
||||
set of interfaces.
|
||||
|
||||
Real-world example
|
||||
Real world example
|
||||
|
||||
> Consider a library system implementing the Abstract Document design pattern in Java, where books can have diverse formats and attributes: physical books, eBooks, and audiobooks. Each format has unique properties, such as page count for physical books, file size for eBooks, and duration for audiobooks. The Abstract Document design pattern allows the library system to manage these diverse formats flexibly. By using this pattern, the system can store and retrieve properties dynamically, without needing a rigid structure for each book type, making it easier to add new formats or attributes in the future without significant changes to the codebase.
|
||||
> Consider a car that consists of multiple parts. However, we don't know if the specific car really has all the parts, or just some of them. Our cars are dynamic and extremely flexible.
|
||||
|
||||
In plain words
|
||||
|
||||
@@ -31,105 +27,94 @@ In plain words
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> An object-oriented structural design pattern for organizing objects in loosely typed key-value stores and exposing the data using typed views. The purpose of the pattern is to achieve a high degree of flexibility between components in a strongly typed language where new properties can be added to the object-tree on the fly, without losing the support of type-safety. The pattern makes use of traits to separate different properties of a class into different interfaces.
|
||||
> An object-oriented structural design pattern for organizing objects in loosely typed key-value stores and exposing
|
||||
the data using typed views. The purpose of the pattern is to achieve a high degree of flexibility between components
|
||||
in a strongly typed language where new properties can be added to the object-tree on the fly, without losing the
|
||||
support of type-safety. The pattern makes use of traits to separate different properties of a class into different
|
||||
interfaces.
|
||||
|
||||
Class diagram
|
||||
**Programmatic Example**
|
||||
|
||||

|
||||
|
||||
|
||||
## Programmatic Example of Abstract Document Pattern in Java
|
||||
|
||||
Consider a car that consists of multiple parts. However, we don't know if the specific car really has all the parts, or just some of them. Our cars are dynamic and extremely flexible.
|
||||
|
||||
Let's first define the base classes `Document` and `AbstractDocument`. They basically make the object hold a property map and any amount of child objects.
|
||||
Let's first define the base classes `Document` and `AbstractDocument`. They basically make the object hold a property
|
||||
map and any amount of child objects.
|
||||
|
||||
```java
|
||||
public interface Document {
|
||||
|
||||
Void put(String key, Object value);
|
||||
Void put(String key, Object value);
|
||||
|
||||
Object get(String key);
|
||||
Object get(String key);
|
||||
|
||||
<T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor);
|
||||
<T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor);
|
||||
}
|
||||
|
||||
public abstract class AbstractDocument implements Document {
|
||||
|
||||
private final Map<String, Object> properties;
|
||||
private final Map<String, Object> properties;
|
||||
|
||||
protected AbstractDocument(Map<String, Object> properties) {
|
||||
Objects.requireNonNull(properties, "properties map is required");
|
||||
this.properties = properties;
|
||||
}
|
||||
protected AbstractDocument(Map<String, Object> properties) {
|
||||
Objects.requireNonNull(properties, "properties map is required");
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void put(String key, Object value) {
|
||||
properties.put(key, value);
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Void put(String key, Object value) {
|
||||
properties.put(key, value);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String key) {
|
||||
return properties.get(key);
|
||||
}
|
||||
@Override
|
||||
public Object get(String key) {
|
||||
return properties.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) {
|
||||
return Stream.ofNullable(get(key))
|
||||
.filter(Objects::nonNull)
|
||||
.map(el -> (List<Map<String, Object>>) el)
|
||||
.findAny()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.map(constructor);
|
||||
}
|
||||
|
||||
// Other properties and methods...
|
||||
@Override
|
||||
public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) {
|
||||
return Stream.ofNullable(get(key))
|
||||
.filter(Objects::nonNull)
|
||||
.map(el -> (List<Map<String, Object>>) el)
|
||||
.findAny()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.map(constructor);
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Next we define an enum `Property` and a set of interfaces for type, price, model and parts. This allows us to create static looking interface to our `Car` class.
|
||||
Next we define an enum `Property` and a set of interfaces for type, price, model and parts. This allows us to create
|
||||
static looking interface to our `Car` class.
|
||||
|
||||
```java
|
||||
public enum Property {
|
||||
|
||||
PARTS, TYPE, PRICE, MODEL
|
||||
PARTS, TYPE, PRICE, MODEL
|
||||
}
|
||||
|
||||
public interface HasType extends Document {
|
||||
|
||||
default Optional<String> getType() {
|
||||
return Optional.ofNullable((String) get(Property.TYPE.toString()));
|
||||
}
|
||||
default Optional<String> getType() {
|
||||
return Optional.ofNullable((String) get(Property.TYPE.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
public interface HasPrice extends Document {
|
||||
|
||||
default Optional<Number> getPrice() {
|
||||
return Optional.ofNullable((Number) get(Property.PRICE.toString()));
|
||||
}
|
||||
default Optional<Number> getPrice() {
|
||||
return Optional.ofNullable((Number) get(Property.PRICE.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
public interface HasModel extends Document {
|
||||
|
||||
default Optional<String> getModel() {
|
||||
return Optional.ofNullable((String) get(Property.MODEL.toString()));
|
||||
}
|
||||
default Optional<String> getModel() {
|
||||
return Optional.ofNullable((String) get(Property.MODEL.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
public interface HasParts extends Document {
|
||||
|
||||
default Stream<Part> getParts() {
|
||||
return children(Property.PARTS.toString(), Part::new);
|
||||
}
|
||||
}
|
||||
|
||||
public class Part extends AbstractDocument implements HasType, HasModel, HasPrice {
|
||||
|
||||
public Part(Map<String, Object> properties) {
|
||||
super(properties);
|
||||
}
|
||||
default Stream<Part> getParts() {
|
||||
return children(Property.PARTS.toString(), Part::new);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -138,32 +123,31 @@ Now we are ready to introduce the `Car`.
|
||||
```java
|
||||
public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {
|
||||
|
||||
public Car(Map<String, Object> properties) {
|
||||
super(properties);
|
||||
}
|
||||
public Car(Map<String, Object> properties) {
|
||||
super(properties);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And finally here's how we construct and use the `Car` in a full example.
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
LOGGER.info("Constructing parts and car");
|
||||
|
||||
var wheelProperties = Map.of(
|
||||
Property.TYPE.toString(), "wheel",
|
||||
Property.MODEL.toString(), "15C",
|
||||
Property.PRICE.toString(), 100L);
|
||||
Property.TYPE.toString(), "wheel",
|
||||
Property.MODEL.toString(), "15C",
|
||||
Property.PRICE.toString(), 100L);
|
||||
|
||||
var doorProperties = Map.of(
|
||||
Property.TYPE.toString(), "door",
|
||||
Property.MODEL.toString(), "Lambo",
|
||||
Property.PRICE.toString(), 300L);
|
||||
Property.TYPE.toString(), "door",
|
||||
Property.MODEL.toString(), "Lambo",
|
||||
Property.PRICE.toString(), 300L);
|
||||
|
||||
var carProperties = Map.of(
|
||||
Property.MODEL.toString(), "300SL",
|
||||
Property.PRICE.toString(), 10000L,
|
||||
Property.PARTS.toString(), List.of(wheelProperties, doorProperties));
|
||||
Property.MODEL.toString(), "300SL",
|
||||
Property.PRICE.toString(), 10000L,
|
||||
Property.PARTS.toString(), List.of(wheelProperties, doorProperties));
|
||||
|
||||
var car = new Car(carProperties);
|
||||
|
||||
@@ -172,28 +156,29 @@ And finally here's how we construct and use the `Car` in a full example.
|
||||
LOGGER.info("-> price: {}", car.getPrice().orElseThrow());
|
||||
LOGGER.info("-> parts: ");
|
||||
car.getParts().forEach(p -> LOGGER.info("\t{}/{}/{}",
|
||||
p.getType().orElse(null),
|
||||
p.getModel().orElse(null),
|
||||
p.getPrice().orElse(null))
|
||||
p.getType().orElse(null),
|
||||
p.getModel().orElse(null),
|
||||
p.getPrice().orElse(null))
|
||||
);
|
||||
}
|
||||
|
||||
// Constructing parts and car
|
||||
// Here is our car:
|
||||
// model: 300SL
|
||||
// price: 10000
|
||||
// parts:
|
||||
// wheel/15C/100
|
||||
// door/Lambo/300
|
||||
```
|
||||
|
||||
The program output:
|
||||
## Class diagram
|
||||
|
||||
```
|
||||
07:21:57.391 [main] INFO com.iluwatar.abstractdocument.App -- Constructing parts and car
|
||||
07:21:57.393 [main] INFO com.iluwatar.abstractdocument.App -- Here is our car:
|
||||
07:21:57.393 [main] INFO com.iluwatar.abstractdocument.App -- -> model: 300SL
|
||||
07:21:57.394 [main] INFO com.iluwatar.abstractdocument.App -- -> price: 10000
|
||||
07:21:57.394 [main] INFO com.iluwatar.abstractdocument.App -- -> parts:
|
||||
07:21:57.395 [main] INFO com.iluwatar.abstractdocument.App -- wheel/15C/100
|
||||
07:21:57.395 [main] INFO com.iluwatar.abstractdocument.App -- door/Lambo/300
|
||||
```
|
||||

|
||||
|
||||
## When to Use the Abstract Document Pattern in Java
|
||||
## Applicability
|
||||
|
||||
The Abstract Document design pattern is especially beneficial in scenarios requiring management of different document types in Java that share some common attributes or behaviors, but also have unique attributes or behaviors specific to their individual types. Here are some scenarios where the Abstract Document design pattern can be applicable:
|
||||
This pattern is particularly useful in scenarios where you have different types of documents that share some common
|
||||
attributes or behaviors, but also have unique attributes or behaviors specific to their individual types. Here are
|
||||
some scenarios where the Abstract Document design pattern can be applicable:
|
||||
|
||||
* Content Management Systems (CMS): In a CMS, you might have various types of content such as articles, images, videos, etc. Each type of content could have shared attributes like creation date, author, and tags, while also having specific attributes like image dimensions for images or video duration for videos.
|
||||
|
||||
@@ -209,39 +194,13 @@ The Abstract Document design pattern is especially beneficial in scenarios requi
|
||||
|
||||
* Project Management Tools: In project management applications, you could have different types of tasks like to-do items, milestones, and issues. The Abstract Document pattern could be used to handle general attributes like task name and assignee, while allowing for specific attributes like milestone date or issue priority.
|
||||
|
||||
* Documents have diverse and evolving attribute structures.
|
||||
The key idea behind the Abstract Document design pattern is to provide a flexible and extensible way to manage different
|
||||
types of documents or entities with shared and distinct attributes. By defining a common interface and implementing it
|
||||
across various document types, you can achieve a more organized and consistent approach to handling complex data
|
||||
structures.
|
||||
|
||||
* Dynamically adding new properties is a common requirement.
|
||||
## Credits
|
||||
|
||||
* Decoupling data access from specific formats is crucial.
|
||||
|
||||
* Maintainability and flexibility are critical for the codebase.
|
||||
|
||||
The key idea behind the Abstract Document design pattern is to provide a flexible and extensible way to manage different types of documents or entities with shared and distinct attributes. By defining a common interface and implementing it across various document types, you can achieve a more organized and consistent approach to handling complex data structures.
|
||||
|
||||
## Benefits and Trade-offs of Abstract Document Pattern
|
||||
|
||||
Benefits:
|
||||
|
||||
* Flexibility: Accommodates varied document structures and properties.
|
||||
|
||||
* Extensibility: Dynamically add new attributes without breaking existing code.
|
||||
|
||||
* Maintainability: Promotes clean and adaptable code due to separation of concerns.
|
||||
|
||||
* Reusability: Typed views enable code reuse for accessing specific attribute types.
|
||||
|
||||
Trade-offs:
|
||||
|
||||
* Complexity: Requires defining interfaces and views, adding implementation overhead.
|
||||
|
||||
* Performance: Might introduce slight performance overhead compared to direct data access.
|
||||
|
||||
## References and Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
|
||||
* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
|
||||
* [Pattern-Oriented Software Architecture Volume 4: A Pattern Language for Distributed Computing (v. 4)](https://amzn.to/49zRP4R)
|
||||
* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR)
|
||||
* [Abstract Document Pattern (Wikipedia)](https://en.wikipedia.org/wiki/Abstract_Document_Pattern)
|
||||
* [Dealing with Properties (Martin Fowler)](http://martinfowler.com/apsupp/properties.pdf)
|
||||
* [Wikipedia: Abstract Document Pattern](https://en.wikipedia.org/wiki/Abstract_Document_Pattern)
|
||||
* [Martin Fowler: Dealing with properties](http://martinfowler.com/apsupp/properties.pdf)
|
||||
* [Pattern-Oriented Software Architecture Volume 4: A Pattern Language for Distributed Computing (v. 4)](https://www.amazon.com/gp/product/0470059028/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0470059028&linkId=e3aacaea7017258acf184f9f3283b492)
|
||||
@@ -34,14 +34,6 @@
|
||||
</parent>
|
||||
<artifactId>abstract-document</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
|
||||
+12
-35
@@ -31,71 +31,48 @@ import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/** Abstract implementation of Document interface. */
|
||||
/**
|
||||
* Abstract implementation of Document interface.
|
||||
*/
|
||||
public abstract class AbstractDocument implements Document {
|
||||
|
||||
private final Map<String, Object> documentProperties;
|
||||
private final Map<String, Object> properties;
|
||||
|
||||
protected AbstractDocument(Map<String, Object> properties) {
|
||||
Objects.requireNonNull(properties, "properties map is required");
|
||||
this.documentProperties = properties;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void put(String key, Object value) {
|
||||
documentProperties.put(key, value);
|
||||
properties.put(key, value);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String key) {
|
||||
return documentProperties.get(key);
|
||||
return properties.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Stream<T> children(String key, Function<Map<String, Object>, T> childConstructor) {
|
||||
public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) {
|
||||
return Stream.ofNullable(get(key))
|
||||
.filter(Objects::nonNull)
|
||||
.map(el -> (List<Map<String, Object>>) el)
|
||||
.findAny()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.map(childConstructor);
|
||||
.map(constructor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return buildStringRepresentation();
|
||||
}
|
||||
|
||||
private String buildStringRepresentation() {
|
||||
var builder = new StringBuilder();
|
||||
builder.append(getClass().getName()).append("[");
|
||||
|
||||
// Explaining variable for document properties map
|
||||
Map<String, Object> documentProperties = this.documentProperties;
|
||||
|
||||
// Explaining variable for the size of document properties map
|
||||
int numProperties = documentProperties.size();
|
||||
|
||||
// Explaining variable for tracking the current property index
|
||||
int currentPropertyIndex = 0;
|
||||
|
||||
// Iterate over document properties map
|
||||
for (Map.Entry<String, Object> entry : documentProperties.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
// Append key-value pair
|
||||
builder.append("[").append(key).append(" : ").append(value).append("]");
|
||||
|
||||
// Add comma if not last property
|
||||
if (++currentPropertyIndex < numProperties) {
|
||||
builder.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
properties.forEach((key, value) -> builder.append("[").append(key).append(" : ").append(value)
|
||||
.append("]"));
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,26 +49,20 @@ public class App {
|
||||
public static void main(String[] args) {
|
||||
LOGGER.info("Constructing parts and car");
|
||||
|
||||
var wheelProperties =
|
||||
Map.of(
|
||||
Property.TYPE.toString(), "wheel",
|
||||
Property.MODEL.toString(), "15C",
|
||||
Property.PRICE.toString(), 100L);
|
||||
var wheelProperties = Map.of(
|
||||
Property.TYPE.toString(), "wheel",
|
||||
Property.MODEL.toString(), "15C",
|
||||
Property.PRICE.toString(), 100L);
|
||||
|
||||
var doorProperties =
|
||||
Map.of(
|
||||
Property.TYPE.toString(), "door",
|
||||
Property.MODEL.toString(), "Lambo",
|
||||
Property.PRICE.toString(), 300L);
|
||||
var doorProperties = Map.of(
|
||||
Property.TYPE.toString(), "door",
|
||||
Property.MODEL.toString(), "Lambo",
|
||||
Property.PRICE.toString(), 300L);
|
||||
|
||||
var carProperties =
|
||||
Map.of(
|
||||
Property.MODEL.toString(),
|
||||
"300SL",
|
||||
Property.PRICE.toString(),
|
||||
10000L,
|
||||
Property.PARTS.toString(),
|
||||
List.of(wheelProperties, doorProperties));
|
||||
var carProperties = Map.of(
|
||||
Property.MODEL.toString(), "300SL",
|
||||
Property.PRICE.toString(), 10000L,
|
||||
Property.PARTS.toString(), List.of(wheelProperties, doorProperties));
|
||||
|
||||
var car = new Car(carProperties);
|
||||
|
||||
@@ -76,13 +70,10 @@ public class App {
|
||||
LOGGER.info("-> model: {}", car.getModel().orElseThrow());
|
||||
LOGGER.info("-> price: {}", car.getPrice().orElseThrow());
|
||||
LOGGER.info("-> parts: ");
|
||||
car.getParts()
|
||||
.forEach(
|
||||
p ->
|
||||
LOGGER.info(
|
||||
"\t{}/{}/{}",
|
||||
p.getType().orElse(null),
|
||||
p.getModel().orElse(null),
|
||||
p.getPrice().orElse(null)));
|
||||
car.getParts().forEach(p -> LOGGER.info("\t{}/{}/{}",
|
||||
p.getType().orElse(null),
|
||||
p.getModel().orElse(null),
|
||||
p.getPrice().orElse(null))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -28,13 +28,15 @@ import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/** Document interface. */
|
||||
/**
|
||||
* Document interface.
|
||||
*/
|
||||
public interface Document {
|
||||
|
||||
/**
|
||||
* Puts the value related to the key.
|
||||
*
|
||||
* @param key element key
|
||||
* @param key element key
|
||||
* @param value element value
|
||||
* @return Void
|
||||
*/
|
||||
@@ -51,7 +53,7 @@ public interface Document {
|
||||
/**
|
||||
* Gets the stream of child documents.
|
||||
*
|
||||
* @param key element key
|
||||
* @param key element key
|
||||
* @param constructor constructor of child class
|
||||
* @return child documents
|
||||
*/
|
||||
|
||||
@@ -27,10 +27,13 @@ package com.iluwatar.abstractdocument.domain;
|
||||
import com.iluwatar.abstractdocument.AbstractDocument;
|
||||
import java.util.Map;
|
||||
|
||||
/** Car entity. */
|
||||
/**
|
||||
* Car entity.
|
||||
*/
|
||||
public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {
|
||||
|
||||
public Car(Map<String, Object> properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,10 +28,13 @@ import com.iluwatar.abstractdocument.Document;
|
||||
import com.iluwatar.abstractdocument.domain.enums.Property;
|
||||
import java.util.Optional;
|
||||
|
||||
/** HasModel trait for static access to 'model' property. */
|
||||
/**
|
||||
* HasModel trait for static access to 'model' property.
|
||||
*/
|
||||
public interface HasModel extends Document {
|
||||
|
||||
default Optional<String> getModel() {
|
||||
return Optional.ofNullable((String) get(Property.MODEL.toString()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,10 +28,13 @@ import com.iluwatar.abstractdocument.Document;
|
||||
import com.iluwatar.abstractdocument.domain.enums.Property;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/** HasParts trait for static access to 'parts' property. */
|
||||
/**
|
||||
* HasParts trait for static access to 'parts' property.
|
||||
*/
|
||||
public interface HasParts extends Document {
|
||||
|
||||
default Stream<Part> getParts() {
|
||||
return children(Property.PARTS.toString(), Part::new);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,10 +28,13 @@ import com.iluwatar.abstractdocument.Document;
|
||||
import com.iluwatar.abstractdocument.domain.enums.Property;
|
||||
import java.util.Optional;
|
||||
|
||||
/** HasPrice trait for static access to 'price' property. */
|
||||
/**
|
||||
* HasPrice trait for static access to 'price' property.
|
||||
*/
|
||||
public interface HasPrice extends Document {
|
||||
|
||||
default Optional<Number> getPrice() {
|
||||
return Optional.ofNullable((Number) get(Property.PRICE.toString()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,10 +28,13 @@ import com.iluwatar.abstractdocument.Document;
|
||||
import com.iluwatar.abstractdocument.domain.enums.Property;
|
||||
import java.util.Optional;
|
||||
|
||||
/** HasType trait for static access to 'type' property. */
|
||||
/**
|
||||
* HasType trait for static access to 'type' property.
|
||||
*/
|
||||
public interface HasType extends Document {
|
||||
|
||||
default Optional<String> getType() {
|
||||
return Optional.ofNullable((String) get(Property.TYPE.toString()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,10 +27,13 @@ package com.iluwatar.abstractdocument.domain;
|
||||
import com.iluwatar.abstractdocument.AbstractDocument;
|
||||
import java.util.Map;
|
||||
|
||||
/** Part entity. */
|
||||
/**
|
||||
* Part entity.
|
||||
*/
|
||||
public class Part extends AbstractDocument implements HasType, HasModel, HasPrice {
|
||||
|
||||
public Part(Map<String, Object> properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
}
|
||||
+5
-5
@@ -24,10 +24,10 @@
|
||||
*/
|
||||
package com.iluwatar.abstractdocument.domain.enums;
|
||||
|
||||
/** Enum To Describe Property type. */
|
||||
/**
|
||||
* Enum To Describe Property type.
|
||||
*/
|
||||
public enum Property {
|
||||
PARTS,
|
||||
TYPE,
|
||||
PRICE,
|
||||
MODEL
|
||||
|
||||
PARTS, TYPE, PRICE, MODEL
|
||||
}
|
||||
+6
-53
@@ -24,14 +24,16 @@
|
||||
*/
|
||||
package com.iluwatar.abstractdocument;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** AbstractDocument test class */
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* AbstractDocument test class
|
||||
*/
|
||||
class AbstractDocumentTest {
|
||||
|
||||
private static final String KEY = "key";
|
||||
@@ -78,53 +80,4 @@ class AbstractDocumentTest {
|
||||
assertTrue(document.toString().contains(VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleExceptionDuringConstruction() {
|
||||
Map<String, Object> invalidProperties =
|
||||
null; // Invalid properties, causing NullPointerException
|
||||
|
||||
// Throw null pointer exception
|
||||
assertThrows(
|
||||
NullPointerException.class,
|
||||
() -> {
|
||||
// Attempt to construct a document with invalid properties
|
||||
new DocumentImplementation(invalidProperties);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPutAndGetNestedDocument() {
|
||||
// Creating a nested document
|
||||
DocumentImplementation nestedDocument = new DocumentImplementation(new HashMap<>());
|
||||
nestedDocument.put("nestedKey", "nestedValue");
|
||||
|
||||
document.put("nested", nestedDocument);
|
||||
|
||||
// Retrieving the nested document
|
||||
DocumentImplementation retrievedNestedDocument =
|
||||
(DocumentImplementation) document.get("nested");
|
||||
|
||||
assertNotNull(retrievedNestedDocument);
|
||||
assertEquals("nestedValue", retrievedNestedDocument.get("nestedKey"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateExistingValue() {
|
||||
// Arrange
|
||||
final String key = "key";
|
||||
final String originalValue = "originalValue";
|
||||
final String updatedValue = "updatedValue";
|
||||
|
||||
// Initializing the value
|
||||
document.put(key, originalValue);
|
||||
|
||||
// Verifying that the initial value is retrieved correctly
|
||||
assertEquals(originalValue, document.get(key));
|
||||
|
||||
// Updating the value
|
||||
document.put(key, updatedValue);
|
||||
|
||||
// Verifying that the updated value is retrieved correctly
|
||||
assertEquals(updatedValue, document.get(key));
|
||||
}
|
||||
}
|
||||
@@ -24,21 +24,25 @@
|
||||
*/
|
||||
package com.iluwatar.abstractdocument;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Simple App test */
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
/**
|
||||
* Simple App test
|
||||
*/
|
||||
class AppTest {
|
||||
|
||||
/**
|
||||
* Issue: Add at least one assertion to this test case.
|
||||
*
|
||||
* <p>Solution: Inserted assertion to check whether the execution of the main method in {@link
|
||||
* App} throws an exception.
|
||||
* Solution: Inserted assertion to check whether the execution of the main method in {@link App}
|
||||
* throws an exception.
|
||||
*/
|
||||
|
||||
@Test
|
||||
void shouldExecuteAppWithoutException() {
|
||||
assertDoesNotThrow(() -> App.main(null));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,16 +24,18 @@
|
||||
*/
|
||||
package com.iluwatar.abstractdocument;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.iluwatar.abstractdocument.domain.Car;
|
||||
import com.iluwatar.abstractdocument.domain.Part;
|
||||
import com.iluwatar.abstractdocument.domain.enums.Property;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Test for Part and Car */
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Test for Part and Car
|
||||
*/
|
||||
class DomainTest {
|
||||
|
||||
private static final String TEST_PART_TYPE = "test-part-type";
|
||||
@@ -45,11 +47,11 @@ class DomainTest {
|
||||
|
||||
@Test
|
||||
void shouldConstructPart() {
|
||||
var partProperties =
|
||||
Map.of(
|
||||
Property.TYPE.toString(), TEST_PART_TYPE,
|
||||
Property.MODEL.toString(), TEST_PART_MODEL,
|
||||
Property.PRICE.toString(), (Object) TEST_PART_PRICE);
|
||||
var partProperties = Map.of(
|
||||
Property.TYPE.toString(), TEST_PART_TYPE,
|
||||
Property.MODEL.toString(), TEST_PART_MODEL,
|
||||
Property.PRICE.toString(), (Object) TEST_PART_PRICE
|
||||
);
|
||||
var part = new Part(partProperties);
|
||||
assertEquals(TEST_PART_TYPE, part.getType().orElseThrow());
|
||||
assertEquals(TEST_PART_MODEL, part.getModel().orElseThrow());
|
||||
@@ -58,14 +60,15 @@ class DomainTest {
|
||||
|
||||
@Test
|
||||
void shouldConstructCar() {
|
||||
var carProperties =
|
||||
Map.of(
|
||||
Property.MODEL.toString(), TEST_CAR_MODEL,
|
||||
Property.PRICE.toString(), TEST_CAR_PRICE,
|
||||
Property.PARTS.toString(), List.of(Map.of(), Map.of()));
|
||||
var carProperties = Map.of(
|
||||
Property.MODEL.toString(), TEST_CAR_MODEL,
|
||||
Property.PRICE.toString(), TEST_CAR_PRICE,
|
||||
Property.PARTS.toString(), List.of(Map.of(), Map.of())
|
||||
);
|
||||
var car = new Car(carProperties);
|
||||
assertEquals(TEST_CAR_MODEL, car.getModel().orElseThrow());
|
||||
assertEquals(TEST_CAR_PRICE, car.getPrice().orElseThrow());
|
||||
assertEquals(2, car.getParts().count());
|
||||
}
|
||||
|
||||
}
|
||||
+132
-130
@@ -1,32 +1,25 @@
|
||||
---
|
||||
title: "Abstract Factory Pattern in Java: Mastering Object Creation Elegantly"
|
||||
shortTitle: Abstract Factory
|
||||
description: "Learn the Abstract Factory pattern in Java with real-world examples, class diagrams, and tutorials. Understand its intent, applicability, benefits, and known uses to enhance your design pattern knowledge."
|
||||
title: Abstract Factory
|
||||
category: Creational
|
||||
language: en
|
||||
tag:
|
||||
- Abstraction
|
||||
- Decoupling
|
||||
- Gang of Four
|
||||
- Instantiation
|
||||
- Polymorphism
|
||||
- Gang of Four
|
||||
---
|
||||
|
||||
## Also known as
|
||||
|
||||
* Kit
|
||||
Kit
|
||||
|
||||
## Intent of Abstract Factory Design Pattern
|
||||
## Intent
|
||||
|
||||
The Abstract Factory pattern in Java provides an interface for creating families of related or dependent objects without specifying their concrete classes, enhancing modularity and flexibility in software design.
|
||||
Provide an interface for creating families of related or dependent
|
||||
objects without specifying their concrete classes.
|
||||
|
||||
## Detailed Explanation of Abstract Factory Pattern with Real-World Examples
|
||||
## Explanation
|
||||
|
||||
Real-world example
|
||||
|
||||
> Imagine a furniture company that uses the Abstract Factory pattern in Java to produce various styles of furniture: modern, Victorian, and rustic. Each style includes products like chairs, tables, and sofas. To ensure consistency within each style, the company uses an Abstract Factory pattern.
|
||||
>
|
||||
> In this scenario, the Abstract Factory is an interface for creating families of related furniture objects (chairs, tables, sofas). Each concrete factory (ModernFurnitureFactory, VictorianFurnitureFactory, RusticFurnitureFactory) implements the Abstract Factory interface and creates a set of products that match the specific style. This way, clients can create a whole set of modern or Victorian furniture without worrying about the details of their instantiation. This maintains a consistent style and allows easy swapping of one style of furniture for another.
|
||||
> To create a kingdom we need objects with a common theme. The elven kingdom needs an elven king, elven castle, and elven army whereas the orcish kingdom needs an orcish king, orcish castle, and orcish army. There is a dependency between the objects in the kingdom.
|
||||
|
||||
In plain words
|
||||
|
||||
@@ -36,93 +29,121 @@ Wikipedia says
|
||||
|
||||
> The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes
|
||||
|
||||
Class diagram
|
||||
**Programmatic Example**
|
||||
|
||||

|
||||
|
||||
## Programmatic Example of Abstract Factory in Java
|
||||
|
||||
To create a kingdom using the Abstract Factory pattern in Java, we need objects with a common theme. The elven kingdom needs an elven king, elven castle, and elven army whereas the orcish kingdom needs an orcish king, orcish castle, and orcish army. There is a dependency between the objects in the kingdom.
|
||||
|
||||
Translating the kingdom example above. First of all, we have some interfaces and implementation for the objects in the kingdom.
|
||||
Translating the kingdom example above. First of all, we have some interfaces and implementation for the objects in the
|
||||
kingdom.
|
||||
|
||||
```java
|
||||
public interface Castle {
|
||||
String getDescription();
|
||||
String getDescription();
|
||||
}
|
||||
|
||||
public interface King {
|
||||
String getDescription();
|
||||
String getDescription();
|
||||
}
|
||||
|
||||
public interface Army {
|
||||
String getDescription();
|
||||
String getDescription();
|
||||
}
|
||||
|
||||
// Elven implementations ->
|
||||
public class ElfCastle implements Castle {
|
||||
static final String DESCRIPTION = "This is the elven castle!";
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
static final String DESCRIPTION = "This is the elven castle!";
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
}
|
||||
|
||||
public class ElfKing implements King {
|
||||
static final String DESCRIPTION = "This is the elven king!";
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
static final String DESCRIPTION = "This is the elven king!";
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
}
|
||||
|
||||
public class ElfArmy implements Army {
|
||||
static final String DESCRIPTION = "This is the elven Army!";
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
static final String DESCRIPTION = "This is the elven Army!";
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
}
|
||||
|
||||
// Orcish implementations similarly -> ...
|
||||
|
||||
```
|
||||
|
||||
Then we have the abstraction and implementations for the kingdom factory.
|
||||
|
||||
```java
|
||||
public interface KingdomFactory {
|
||||
Castle createCastle();
|
||||
|
||||
King createKing();
|
||||
|
||||
Army createArmy();
|
||||
Castle createCastle();
|
||||
King createKing();
|
||||
Army createArmy();
|
||||
}
|
||||
|
||||
public class ElfKingdomFactory implements KingdomFactory {
|
||||
|
||||
@Override
|
||||
public Castle createCastle() {
|
||||
return new ElfCastle();
|
||||
}
|
||||
@Override
|
||||
public Castle createCastle() {
|
||||
return new ElfCastle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public King createKing() {
|
||||
return new ElfKing();
|
||||
}
|
||||
@Override
|
||||
public King createKing() {
|
||||
return new ElfKing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Army createArmy() {
|
||||
return new ElfArmy();
|
||||
}
|
||||
@Override
|
||||
public Army createArmy() {
|
||||
return new ElfArmy();
|
||||
}
|
||||
}
|
||||
|
||||
// Orcish implementations similarly -> ...
|
||||
public class OrcKingdomFactory implements KingdomFactory {
|
||||
|
||||
@Override
|
||||
public Castle createCastle() {
|
||||
return new OrcCastle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public King createKing() {
|
||||
return new OrcKing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Army createArmy() {
|
||||
return new OrcArmy();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, we can design a factory for our different kingdom factories. In this example, we created `FactoryMaker`, responsible for returning an instance of either `ElfKingdomFactory` or `OrcKingdomFactory`. The client can use `FactoryMaker` to create the desired concrete factory which, in turn, will produce different concrete objects (derived from `Army`, `King`, `Castle`). In this example, we also used an enum to parameterize which type of kingdom factory the client will ask for.
|
||||
Now we have the abstract factory that lets us make a family of related objects i.e. elven kingdom factory creates elven castle, king and army, etc.
|
||||
|
||||
```java
|
||||
var factory = new ElfKingdomFactory();
|
||||
var castle = factory.createCastle();
|
||||
var king = factory.createKing();
|
||||
var army = factory.createArmy();
|
||||
|
||||
castle.getDescription();
|
||||
king.getDescription();
|
||||
army.getDescription();
|
||||
```
|
||||
|
||||
Program output:
|
||||
|
||||
```java
|
||||
This is the elven castle!
|
||||
This is the elven king!
|
||||
This is the elven Army!
|
||||
```
|
||||
|
||||
Now, we can design a factory for our different kingdom factories. In this example, we created `FactoryMaker`, responsible for returning an instance of either `ElfKingdomFactory` or `OrcKingdomFactory`.
|
||||
The client can use `FactoryMaker` to create the desired concrete factory which, in turn, will produce different concrete objects (derived from `Army`, `King`, `Castle`).
|
||||
In this example, we also used an enum to parameterize which type of kingdom factory the client will ask for.
|
||||
|
||||
```java
|
||||
public static class FactoryMaker {
|
||||
@@ -135,93 +156,74 @@ public static class FactoryMaker {
|
||||
return switch (type) {
|
||||
case ELF -> new ElfKingdomFactory();
|
||||
case ORC -> new OrcKingdomFactory();
|
||||
default -> throw new IllegalArgumentException("KingdomType not supported.");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
var app = new App();
|
||||
|
||||
LOGGER.info("Elf Kingdom");
|
||||
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF));
|
||||
LOGGER.info(app.getArmy().getDescription());
|
||||
LOGGER.info(app.getCastle().getDescription());
|
||||
LOGGER.info(app.getKing().getDescription());
|
||||
|
||||
LOGGER.info("Orc Kingdom");
|
||||
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC));
|
||||
--similar use of the orc factory
|
||||
}
|
||||
```
|
||||
|
||||
Here is the main function of our example application:
|
||||
## Class diagram
|
||||
|
||||
```java
|
||||
LOGGER.info("elf kingdom");
|
||||
createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
|
||||
LOGGER.info(kingdom.getArmy().getDescription());
|
||||
LOGGER.info(kingdom.getCastle().getDescription());
|
||||
LOGGER.info(kingdom.getKing().getDescription());
|
||||

|
||||
|
||||
LOGGER.info("orc kingdom");
|
||||
createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
|
||||
LOGGER.info(kingdom.getArmy().getDescription());
|
||||
LOGGER.info(kingdom.getCastle().getDescription());
|
||||
LOGGER.info(kingdom.getKing().getDescription());
|
||||
```
|
||||
|
||||
The program output:
|
||||
## Applicability
|
||||
|
||||
```
|
||||
07:35:46.340 [main] INFO com.iluwatar.abstractfactory.App -- elf kingdom
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven army!
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven castle!
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven king!
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- orc kingdom
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc army!
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc castle!
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc king!
|
||||
```
|
||||
Use the Abstract Factory pattern when
|
||||
|
||||
## When to Use the Abstract Factory Pattern in Java
|
||||
* The system should be independent of how its products are created, composed, and represented
|
||||
* The system should be configured with one of the multiple families of products
|
||||
* The family of related product objects is designed to be used together, and you need to enforce this constraint
|
||||
* You want to provide a class library of products, and you want to reveal just their interfaces, not their implementations
|
||||
* The lifetime of the dependency is conceptually shorter than the lifetime of the consumer.
|
||||
* You need a run-time value to construct a particular dependency
|
||||
* You want to decide which product to call from a family at runtime.
|
||||
* You need to supply one or more parameters only known at run-time before you can resolve a dependency.
|
||||
* When you need consistency among products
|
||||
* You don’t want to change existing code when adding new products or families of products to the program.
|
||||
|
||||
Use the Abstract Factory pattern in Java when:
|
||||
Example use cases
|
||||
|
||||
* The system should be independent of how its products are created, composed, and represented.
|
||||
* You need to configure the system with one of multiple families of products.
|
||||
* A family of related product objects must be used together, enforcing consistency.
|
||||
* You want to provide a class library of products, exposing only their interfaces, not their implementations.
|
||||
* The lifetime of dependencies is shorter than the consumer's lifetime.
|
||||
* Dependencies need to be constructed using runtime values or parameters.
|
||||
* You need to choose which product to use from a family at runtime.
|
||||
* Adding new products or families should not require changes to existing code.
|
||||
* Selecting to call to the appropriate implementation of FileSystemAcmeService or DatabaseAcmeService or NetworkAcmeService at runtime.
|
||||
* Unit test case writing becomes much easier
|
||||
* UI tools for different OS
|
||||
|
||||
## Abstract Factory Pattern Java Tutorials
|
||||
## Consequences
|
||||
|
||||
* [Abstract Factory Design Pattern in Java (DigitalOcean)](https://www.digitalocean.com/community/tutorials/abstract-factory-design-pattern-in-java)
|
||||
* [Abstract Factory(Refactoring Guru)](https://refactoring.guru/design-patterns/abstract-factory)
|
||||
* Dependency injection in java hides the service class dependencies that can lead to runtime errors that would have been caught at compile time.
|
||||
* While the pattern is great when creating predefined objects, adding the new ones might be challenging.
|
||||
* The code becomes more complicated than it should be since a lot of new interfaces and classes are introduced along with the pattern.
|
||||
|
||||
## Benefits and Trade-offs of Abstract Factory Pattern
|
||||
## Tutorials
|
||||
|
||||
Benefits:
|
||||
* [Abstract Factory Pattern Tutorial](https://www.journaldev.com/1418/abstract-factory-design-pattern-in-java)
|
||||
|
||||
* Flexibility: Easily switch between product families without code modifications.
|
||||
## Known uses
|
||||
|
||||
* Decoupling: Client code only interacts with abstract interfaces, promoting portability and maintainability.
|
||||
|
||||
* Reusability: Abstract factories and products facilitate component reuse across projects.
|
||||
|
||||
* Maintainability: Changes to individual product families are localized, simplifying updates.
|
||||
|
||||
Trade-offs:
|
||||
|
||||
* Complexity: Defining abstract interfaces and concrete factories adds initial overhead.
|
||||
|
||||
* Indirectness: Client code interacts with products indirectly through factories, potentially reducing transparency.
|
||||
|
||||
## Real-World Applications of Abstract Factory Pattern in Java
|
||||
|
||||
* Java Swing's `LookAndFeel` classes for providing different look-and-feel options.
|
||||
* Various implementations in the Java Abstract Window Toolkit (AWT) for creating different GUI components.
|
||||
* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
|
||||
* [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--)
|
||||
* [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--)
|
||||
|
||||
## Related Java Design Patterns
|
||||
## Related patterns
|
||||
|
||||
* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): Abstract Factory uses Factory Methods to create products.
|
||||
* [Singleton](https://java-design-patterns.com/patterns/singleton/): Abstract Factory classes are often implemented as Singletons.
|
||||
* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/): Similar to Abstract Factory but focuses on configuring and managing a set of related objects in a flexible way.
|
||||
* [Factory Method](https://java-design-patterns.com/patterns/factory-method/)
|
||||
* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/)
|
||||
|
||||
## References and Credits
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
|
||||
* [Design Patterns in Java](https://amzn.to/3Syw0vC)
|
||||
* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
|
||||
* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3HWNf4U)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
@@ -34,14 +34,6 @@
|
||||
</parent>
|
||||
<artifactId>abstract-factory</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
@@ -42,11 +41,14 @@ import lombok.extern.slf4j.Slf4j;
|
||||
* both concrete implementations to create a king, a castle, and an army.
|
||||
*/
|
||||
@Slf4j
|
||||
@Getter
|
||||
public class App implements Runnable {
|
||||
|
||||
private final Kingdom kingdom = new Kingdom();
|
||||
|
||||
public Kingdom getKingdom() {
|
||||
return kingdom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Program entry point.
|
||||
*
|
||||
@@ -74,7 +76,6 @@ public class App implements Runnable {
|
||||
|
||||
/**
|
||||
* Creates kingdom.
|
||||
*
|
||||
* @param kingdomType type of Kingdom
|
||||
*/
|
||||
public void createKingdom(final Kingdom.FactoryMaker.KingdomType kingdomType) {
|
||||
@@ -83,4 +84,4 @@ public class App implements Runnable {
|
||||
kingdom.setCastle(kingdomFactory.createCastle());
|
||||
kingdom.setArmy(kingdomFactory.createArmy());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
/** Army interface. */
|
||||
/**
|
||||
* Army interface.
|
||||
*/
|
||||
public interface Army {
|
||||
|
||||
String getDescription();
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
/** Castle interface. */
|
||||
/**
|
||||
* Castle interface.
|
||||
*/
|
||||
public interface Castle {
|
||||
|
||||
String getDescription();
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
/** ElfArmy. */
|
||||
/**
|
||||
* ElfArmy.
|
||||
*/
|
||||
public class ElfArmy implements Army {
|
||||
|
||||
static final String DESCRIPTION = "This is the elven army!";
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
/** ElfCastle. */
|
||||
/**
|
||||
* ElfCastle.
|
||||
*/
|
||||
public class ElfCastle implements Castle {
|
||||
|
||||
static final String DESCRIPTION = "This is the elven castle!";
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
/** ElfKing. */
|
||||
/**
|
||||
* ElfKing.
|
||||
*/
|
||||
public class ElfKing implements King {
|
||||
|
||||
static final String DESCRIPTION = "This is the elven king!";
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
/** ElfKingdomFactory concrete factory. */
|
||||
/**
|
||||
* ElfKingdomFactory concrete factory.
|
||||
*/
|
||||
public class ElfKingdomFactory implements KingdomFactory {
|
||||
|
||||
@Override
|
||||
@@ -41,4 +43,5 @@ public class ElfKingdomFactory implements KingdomFactory {
|
||||
public Army createArmy() {
|
||||
return new ElfArmy();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
/** King interface. */
|
||||
/**
|
||||
* King interface.
|
||||
*/
|
||||
public interface King {
|
||||
|
||||
String getDescription();
|
||||
|
||||
@@ -27,7 +27,9 @@ package com.iluwatar.abstractfactory;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/** Helper class to manufacture {@link KingdomFactory} beans. */
|
||||
/**
|
||||
* Helper class to manufacture {@link KingdomFactory} beans.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class Kingdom {
|
||||
@@ -36,20 +38,26 @@ public class Kingdom {
|
||||
private Castle castle;
|
||||
private Army army;
|
||||
|
||||
/** The factory of kingdom factories. */
|
||||
/**
|
||||
* The factory of kingdom factories.
|
||||
*/
|
||||
public static class FactoryMaker {
|
||||
|
||||
/** Enumeration for the different types of Kingdoms. */
|
||||
/**
|
||||
* Enumeration for the different types of Kingdoms.
|
||||
*/
|
||||
public enum KingdomType {
|
||||
ELF,
|
||||
ORC
|
||||
ELF, ORC
|
||||
}
|
||||
|
||||
/** The factory method to create KingdomFactory concrete objects. */
|
||||
/**
|
||||
* The factory method to create KingdomFactory concrete objects.
|
||||
*/
|
||||
public static KingdomFactory makeFactory(KingdomType type) {
|
||||
return switch (type) {
|
||||
case ELF -> new ElfKingdomFactory();
|
||||
case ORC -> new OrcKingdomFactory();
|
||||
default -> throw new IllegalArgumentException("KingdomType not supported.");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
/** KingdomFactory factory interface. */
|
||||
/**
|
||||
* KingdomFactory factory interface.
|
||||
*/
|
||||
public interface KingdomFactory {
|
||||
|
||||
Castle createCastle();
|
||||
@@ -32,4 +34,5 @@ public interface KingdomFactory {
|
||||
King createKing();
|
||||
|
||||
Army createArmy();
|
||||
|
||||
}
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
/** OrcArmy. */
|
||||
/**
|
||||
* OrcArmy.
|
||||
*/
|
||||
public class OrcArmy implements Army {
|
||||
|
||||
static final String DESCRIPTION = "This is the orc army!";
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
/** OrcCastle. */
|
||||
/**
|
||||
* OrcCastle.
|
||||
*/
|
||||
public class OrcCastle implements Castle {
|
||||
|
||||
static final String DESCRIPTION = "This is the orc castle!";
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
/** OrcKing. */
|
||||
/**
|
||||
* OrcKing.
|
||||
*/
|
||||
public class OrcKing implements King {
|
||||
|
||||
static final String DESCRIPTION = "This is the orc king!";
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
/** OrcKingdomFactory concrete factory. */
|
||||
/**
|
||||
* OrcKingdomFactory concrete factory.
|
||||
*/
|
||||
public class OrcKingdomFactory implements KingdomFactory {
|
||||
|
||||
@Override
|
||||
|
||||
+5
-3
@@ -24,12 +24,14 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for abstract factory. */
|
||||
/**
|
||||
* Tests for abstract factory.
|
||||
*/
|
||||
class AbstractFactoryTest {
|
||||
|
||||
private final App app = new App();
|
||||
|
||||
@@ -24,16 +24,18 @@
|
||||
*/
|
||||
package com.iluwatar.abstractfactory;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Check whether the execution of the main method in {@link App} throws an exception. */
|
||||
class AppTest {
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
/**
|
||||
* Check whether the execution of the main method in {@link App} throws an exception.
|
||||
*/
|
||||
class AppTest {
|
||||
|
||||
@Test
|
||||
void shouldExecuteApplicationWithoutException() {
|
||||
|
||||
assertDoesNotThrow(() -> App.main(new String[] {}));
|
||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
||||
}
|
||||
}
|
||||
+87
-182
@@ -1,222 +1,127 @@
|
||||
---
|
||||
title: "Active Object Pattern in Java: Achieving Efficient Asynchronous Processing"
|
||||
shortTitle: Active Object
|
||||
description: "Learn about the Active Object design pattern in Java. This guide covers asynchronous behavior, concurrency, and practical examples to enhance your Java applications' performance."
|
||||
title: Active Object
|
||||
category: Concurrency
|
||||
language: en
|
||||
tag:
|
||||
- Asynchronous
|
||||
- Decoupling
|
||||
- Messaging
|
||||
- Synchronization
|
||||
- Thread management
|
||||
- Performance
|
||||
---
|
||||
|
||||
## Intent of Active Object Design Pattern
|
||||
|
||||
The Active Object pattern provides a reliable method for asynchronous processing in Java, ensuring responsive applications and efficient thread management. It achieves this by encapsulating tasks within objects that have their own thread and message queue. This separation keeps the main thread responsive and avoids issues like direct thread manipulation or shared state access.
|
||||
## Intent
|
||||
The active object design pattern decouples method execution from method invocation for objects that each reside in their thread of control. The goal is to introduce concurrency, by using asynchronous method invocation, and a scheduler for handling requests.
|
||||
|
||||
## Detailed Explanation of Active Object Pattern with Real-World Examples
|
||||
## Explanation
|
||||
|
||||
The class that implements the active object pattern will contain a self-synchronization mechanism without using 'synchronized' methods.
|
||||
|
||||
Real-world example
|
||||
|
||||
> Imagine a busy restaurant where customers place orders with waiters. Instead of the waiters going to the kitchen to prepare the food themselves, they write the orders on slips and hand them to a dispatcher. The dispatcher manages a pool of chefs who prepare the meals asynchronously. Once a chef is free, they pick up the next order from the queue, prepare the dish, and notify the waiter when it's ready for serving.
|
||||
>
|
||||
> In this analogy, the waiters represent the client threads, the dispatcher represents the scheduler, and the chefs represent the method execution in separate threads. This setup allows the waiters to continue taking orders without being blocked by the food preparation process, much like the Active Object pattern decouples method invocation from execution to enhance concurrency.
|
||||
>The Orcs are known for their wildness and untameable soul. It seems like they have their own thread of control based on previous behavior.
|
||||
|
||||
In plain words
|
||||
|
||||
> The Active Object pattern decouples method execution from method invocation to improve concurrency and responsiveness in multithreaded applications.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> The active object design pattern decouples method execution from method invocation for objects that each reside in their own thread of control.[1] The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests.
|
||||
>
|
||||
> The pattern consists of six elements:
|
||||
>
|
||||
> * A proxy, which provides an interface towards clients with publicly accessible methods.
|
||||
> * An interface which defines the method request on an active object.
|
||||
> * A list of pending requests from clients.
|
||||
> * A scheduler, which decides which request to execute next.
|
||||
> * The implementation of the active object method.
|
||||
> * A callback or variable for the client to receive the result.
|
||||
|
||||
Sequence diagram
|
||||
|
||||

|
||||
To implement a creature that has its own thread of control mechanism and expose its API only and not the execution itself, we can use the Active Object pattern.
|
||||
|
||||
|
||||
## Programmatic Example of Active Object in Java
|
||||
|
||||
This section explains how the Active Object design pattern works in Java, highlighting its use in asynchronous task management and concurrency control.
|
||||
|
||||
The Orcs are known for their wildness and untameable soul. It seems like they have their own thread of control based on previous behavior. To implement a creature that has its own thread of control mechanism and expose its API only and not the execution itself, we can use the Active Object pattern.
|
||||
**Programmatic Example**
|
||||
|
||||
```java
|
||||
public abstract class ActiveCreature {
|
||||
private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
|
||||
public abstract class ActiveCreature{
|
||||
private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
|
||||
|
||||
private BlockingQueue<Runnable> requests;
|
||||
private BlockingQueue<Runnable> requests;
|
||||
|
||||
private String name;
|
||||
|
||||
private Thread thread;
|
||||
|
||||
private String name;
|
||||
|
||||
private Thread thread;
|
||||
|
||||
public ActiveCreature(String name) {
|
||||
this.name = name;
|
||||
this.requests = new LinkedBlockingQueue<Runnable>();
|
||||
thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
requests.take().run();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
public ActiveCreature(String name) {
|
||||
this.name = name;
|
||||
this.requests = new LinkedBlockingQueue<Runnable>();
|
||||
thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
requests.take().run();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public void eat() throws InterruptedException {
|
||||
requests.put(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("{} is eating!",name());
|
||||
logger.info("{} has finished eating!",name());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void eat() throws InterruptedException {
|
||||
requests.put(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("{} is eating!", name());
|
||||
logger.info("{} has finished eating!", name());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void roam() throws InterruptedException {
|
||||
requests.put(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("{} has started to roam the wastelands.", name());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
public void roam() throws InterruptedException {
|
||||
requests.put(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("{} has started to roam the wastelands.",name());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We can see that any class that will extend the `ActiveCreature` class will have its own thread of control to invoke and execute methods.
|
||||
We can see that any class that will extend the ActiveCreature class will have its own thread of control to invoke and execute methods.
|
||||
|
||||
For example, the `Orc` class:
|
||||
For example, the Orc class:
|
||||
|
||||
```java
|
||||
public class Orc extends ActiveCreature {
|
||||
|
||||
public Orc(String name) {
|
||||
super(name);
|
||||
}
|
||||
public Orc(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Now, we can create multiple creatures such as orcs, tell them to eat and roam, and they will execute it on their own thread of control:
|
||||
Now, we can create multiple creatures such as Orcs, tell them to eat and roam, and they will execute it on their own thread of control:
|
||||
|
||||
```java
|
||||
public class App implements Runnable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(App.class.getName());
|
||||
|
||||
private static final int NUM_CREATURES = 3;
|
||||
|
||||
public static void main(String[] args) {
|
||||
var app = new App();
|
||||
app.run();
|
||||
public static void main(String[] args) {
|
||||
var app = new App();
|
||||
app.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ActiveCreature creature;
|
||||
try {
|
||||
for (int i = 0;i < creatures;i++) {
|
||||
creature = new Orc(Orc.class.getSimpleName().toString() + i);
|
||||
creature.eat();
|
||||
creature.roam();
|
||||
}
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
List<ActiveCreature> creatures = new ArrayList<>();
|
||||
try {
|
||||
for (int i = 0; i < NUM_CREATURES; i++) {
|
||||
creatures.add(new Orc(Orc.class.getSimpleName() + i));
|
||||
creatures.get(i).eat();
|
||||
creatures.get(i).roam();
|
||||
}
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
for (int i = 0; i < NUM_CREATURES; i++) {
|
||||
creatures.get(i).kill(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Runtime.getRuntime().exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
Program output:
|
||||
## Class diagram
|
||||
|
||||
```
|
||||
09:00:02.501 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 is eating!
|
||||
09:00:02.501 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 is eating!
|
||||
09:00:02.501 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 is eating!
|
||||
09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has finished eating!
|
||||
09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has finished eating!
|
||||
09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has started to roam in the wastelands.
|
||||
09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has finished eating!
|
||||
09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has started to roam in the wastelands.
|
||||
09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has started to roam in the wastelands.
|
||||
```
|
||||

|
||||
|
||||
## When to Use the Active Object Pattern in Java
|
||||
## Tutorials
|
||||
|
||||
Use the Active Object pattern in Java when:
|
||||
|
||||
* when you need to handle asynchronous tasks without blocking the main thread, ensuring better performance and responsiveness.
|
||||
* When you need to interact with external resources asynchronously.
|
||||
* When you want to improve the responsiveness of your application.
|
||||
* When you need to manage concurrent tasks in a modular and maintainable way.
|
||||
|
||||
## Active Object Pattern Java Tutorials
|
||||
|
||||
* [Android and Java Concurrency: The Active Object Pattern(Douglas Schmidt)](https://www.youtube.com/watch?v=Cd8t2u5Qmvc)
|
||||
|
||||
## Real-World Applications of Active Object Pattern in Java
|
||||
|
||||
* Real-time trading systems where transaction requests are handled asynchronously.
|
||||
* GUIs where long-running tasks are executed in the background without freezing the user interface.
|
||||
* Game programming to handle concurrent updates to game state or AI computations.
|
||||
|
||||
## Benefits and Trade-offs of Active Object Pattern
|
||||
|
||||
Discover the benefits and trade-offs of using the Active Object pattern in Java, including improved thread safety and potential overhead concerns.
|
||||
|
||||
Benefits:
|
||||
|
||||
* Improves responsiveness of the main thread.
|
||||
* Encapsulates concurrency concerns within objects.
|
||||
* Promotes better code organization and maintainability.
|
||||
* Provides thread safety and avoids shared state access problems.
|
||||
|
||||
Trade-offs:
|
||||
|
||||
* Introduces additional overhead due to message passing and thread management.
|
||||
* May not be suitable for all types of concurrency problems.
|
||||
|
||||
## Related Java Design Patterns
|
||||
|
||||
* [Command](https://java-design-patterns.com/patterns/command/): Encapsulates a request as an object, similarly to how the Active Object pattern encapsulates method calls.
|
||||
* [Promise](https://java-design-patterns.com/patterns/promise/): Provides a means to retrieve the result of an asynchronous method call, often used in conjunction with Active Object.
|
||||
* [Proxy](https://java-design-patterns.com/patterns/proxy/): The Active Object pattern can use a proxy to handle method invocations asynchronously.
|
||||
|
||||
## References and Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object Software](https://amzn.to/3HYqrBE)
|
||||
* [Concurrent Programming in Java: Design Principles and Patterns](https://amzn.to/498SRVq)
|
||||
* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
|
||||
* [Learning Concurrent Programming in Scala](https://amzn.to/3UE07nV)
|
||||
* [Pattern Languages of Program Design 3](https://amzn.to/3OI1j61)
|
||||
* [Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://amzn.to/3UgC24V)
|
||||
* [Android and Java Concurrency: The Active Object Pattern](https://www.youtube.com/watch?v=Cd8t2u5Qmvc)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB |
@@ -34,14 +34,6 @@
|
||||
</parent>
|
||||
<artifactId>active-object</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
|
||||
@@ -29,87 +29,87 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/** ActiveCreature class is the base of the active object example. */
|
||||
/**
|
||||
* ActiveCreature class is the base of the active object example.
|
||||
* @author Noam Greenshtain
|
||||
*
|
||||
*/
|
||||
public abstract class ActiveCreature {
|
||||
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
|
||||
|
||||
private BlockingQueue<Runnable> requests;
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
|
||||
private Thread thread; // Thread of execution.
|
||||
|
||||
|
||||
private int status; // status of the thread of execution.
|
||||
|
||||
/** Constructor and initialization. */
|
||||
/**
|
||||
* Constructor and initialization.
|
||||
*/
|
||||
protected ActiveCreature(String name) {
|
||||
this.name = name;
|
||||
this.status = 0;
|
||||
this.requests = new LinkedBlockingQueue<>();
|
||||
thread =
|
||||
new Thread(
|
||||
() -> {
|
||||
boolean infinite = true;
|
||||
while (infinite) {
|
||||
try {
|
||||
requests.take().run();
|
||||
} catch (InterruptedException e) {
|
||||
if (this.status != 0) {
|
||||
logger.error("Thread was interrupted. --> {}", e.getMessage());
|
||||
}
|
||||
infinite = false;
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
});
|
||||
thread = new Thread(() -> {
|
||||
boolean infinite = true;
|
||||
while (infinite) {
|
||||
try {
|
||||
requests.take().run();
|
||||
} catch (InterruptedException e) {
|
||||
if (this.status != 0) {
|
||||
logger.error("Thread was interrupted. --> {}", e.getMessage());
|
||||
}
|
||||
infinite = false;
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Eats the porridge.
|
||||
*
|
||||
* @throws InterruptedException due to firing a new Runnable.
|
||||
*/
|
||||
public void eat() throws InterruptedException {
|
||||
requests.put(
|
||||
() -> {
|
||||
logger.info("{} is eating!", name());
|
||||
logger.info("{} has finished eating!", name());
|
||||
});
|
||||
requests.put(() -> {
|
||||
logger.info("{} is eating!", name());
|
||||
logger.info("{} has finished eating!", name());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Roam the wastelands.
|
||||
*
|
||||
* @throws InterruptedException due to firing a new Runnable.
|
||||
*/
|
||||
public void roam() throws InterruptedException {
|
||||
requests.put(() -> logger.info("{} has started to roam in the wastelands.", name()));
|
||||
requests.put(() ->
|
||||
logger.info("{} has started to roam in the wastelands.", name())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of the creature.
|
||||
*
|
||||
* @return the name of the creature.
|
||||
*/
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Kills the thread of execution.
|
||||
*
|
||||
* @param status of the thread of execution. 0 == OK, the rest is logging an error.
|
||||
*/
|
||||
public void kill(int status) {
|
||||
this.status = status;
|
||||
this.thread.interrupt();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the status of the thread of execution.
|
||||
*
|
||||
* @return the status of the thread of execution.
|
||||
*/
|
||||
public int getStatus() {
|
||||
|
||||
@@ -30,17 +30,17 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The Active Object pattern helps to solve synchronization difficulties without using
|
||||
* 'synchronized' methods. The active object will contain a thread-safe data structure (such as
|
||||
* BlockingQueue) and use to synchronize method calls by moving the logic of the method into an
|
||||
* invocator(usually a Runnable) and store it in the DSA.
|
||||
*
|
||||
* The Active Object pattern helps to solve synchronization difficulties without using
|
||||
* 'synchronized' methods. The active object will contain a thread-safe data structure
|
||||
* (such as BlockingQueue) and use to synchronize method calls by moving the logic of the method
|
||||
* into an invocator(usually a Runnable) and store it in the DSA.
|
||||
*
|
||||
* <p>In this example, we fire 20 threads to modify a value in the target class.
|
||||
*/
|
||||
public class App implements Runnable {
|
||||
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(App.class.getName());
|
||||
|
||||
|
||||
private static final int NUM_CREATURES = 3;
|
||||
|
||||
/**
|
||||
@@ -48,11 +48,11 @@ public class App implements Runnable {
|
||||
*
|
||||
* @param args command line arguments.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
public static void main(String[] args) {
|
||||
var app = new App();
|
||||
app.run();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
List<ActiveCreature> creatures = new ArrayList<>();
|
||||
|
||||
@@ -24,10 +24,15 @@
|
||||
*/
|
||||
package com.iluwatar.activeobject;
|
||||
|
||||
/** An implementation of the ActiveCreature class. */
|
||||
/**
|
||||
* An implementation of the ActiveCreature class.
|
||||
* @author Noam Greenshtain
|
||||
*
|
||||
*/
|
||||
public class Orc extends ActiveCreature {
|
||||
|
||||
public Orc(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,16 +27,17 @@ package com.iluwatar.activeobject;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ActiveCreatureTest {
|
||||
|
||||
@Test
|
||||
void executionTest() throws InterruptedException {
|
||||
ActiveCreature orc = new Orc("orc1");
|
||||
assertEquals("orc1",orc.name());
|
||||
assertEquals(0,orc.getStatus());
|
||||
orc.eat();
|
||||
orc.roam();
|
||||
orc.kill(0);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void executionTest() throws InterruptedException {
|
||||
ActiveCreature orc = new Orc("orc1");
|
||||
assertEquals("orc1", orc.name());
|
||||
assertEquals(0, orc.getStatus());
|
||||
orc.eat();
|
||||
orc.roam();
|
||||
orc.kill(0);
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,11 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
class AppTest {
|
||||
|
||||
@Test
|
||||
void shouldExecuteApplicationWithoutException() {
|
||||
assertDoesNotThrow(() -> App.main(new String[] {}));
|
||||
}
|
||||
@Test
|
||||
void shouldExecuteApplicationWithoutException() {
|
||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
---
|
||||
title: "Actor Model Pattern in Java: Building Concurrent Systems with Elegance"
|
||||
shortTitle: Actor Model
|
||||
description: "Explore the Actor Model pattern in Java with real-world examples and practical implementation. Learn how to build scalable, message-driven systems using actors, messages, and asynchronous communication."
|
||||
category: Concurrency
|
||||
language: en
|
||||
tag:
|
||||
- Concurrency
|
||||
- Messaging
|
||||
- Isolation
|
||||
- Asynchronous
|
||||
- Distributed Systems
|
||||
- Actor Model
|
||||
---
|
||||
|
||||
## Also Known As
|
||||
|
||||
- Message-passing concurrency
|
||||
- Actor-based concurrency
|
||||
|
||||
---
|
||||
|
||||
## Intent of Actor Model Pattern
|
||||
|
||||
The Actor Model pattern enables the construction of highly concurrent, distributed, and fault-tolerant systems by using isolated components (actors) that interact exclusively through asynchronous message passing.
|
||||
|
||||
---
|
||||
|
||||
## Detailed Explanation of Actor Model Pattern with Real-World Examples
|
||||
|
||||
### 📦 Real-world Example
|
||||
|
||||
Imagine a customer service system:
|
||||
- Each **customer support agent** is an **actor**.
|
||||
- Customers **send questions (messages)** to agents.
|
||||
- Each agent handles one request at a time and can **respond asynchronously** without interfering with other agents.
|
||||
|
||||
---
|
||||
|
||||
### 🧠 In Plain Words
|
||||
|
||||
> "Actors are like independent workers that never share memory and only communicate through messages."
|
||||
|
||||
---
|
||||
|
||||
### 📖 Wikipedia Says
|
||||
|
||||
> [Actor model](https://en.wikipedia.org/wiki/Actor_model) is a mathematical model of concurrent computation that treats "actors" as the universal primitives of concurrent computation.
|
||||
|
||||
---
|
||||
|
||||
### 🧹 Architecture Diagram
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Programmatic Example of Actor Model Pattern in Java
|
||||
|
||||
### Actor.java
|
||||
|
||||
```java
|
||||
public abstract class Actor implements Runnable {
|
||||
|
||||
@Setter @Getter private String actorId;
|
||||
private final BlockingQueue<Message> mailbox = new LinkedBlockingQueue<>();
|
||||
private volatile boolean active = true;
|
||||
|
||||
|
||||
public void send(Message message) {
|
||||
mailbox.add(message);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
}
|
||||
|
||||
protected abstract void onReceive(Message message);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Message.java
|
||||
|
||||
```java
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class Message {
|
||||
private final String content;
|
||||
private final String senderId;
|
||||
}
|
||||
```
|
||||
|
||||
### ActorSystem.java
|
||||
|
||||
```java
|
||||
public class ActorSystem {
|
||||
public void startActor(Actor actor) {
|
||||
String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
|
||||
actor.setActorId(actorId); // assign the actor it's ID
|
||||
actorRegister.put(actorId, actor); // Register and save the actor with it's ID
|
||||
executor.submit(actor); // Run the actor in a thread
|
||||
}
|
||||
public Actor getActorById(String actorId) {
|
||||
return actorRegister.get(actorId); // Find by Id
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
executor.shutdownNow(); // Stop all threads
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### App.java
|
||||
|
||||
```java
|
||||
public class App {
|
||||
public static void main(String[] args) {
|
||||
ActorSystem system = new ActorSystem();
|
||||
Actor srijan = new ExampleActor(system);
|
||||
Actor ansh = new ExampleActor2(system);
|
||||
|
||||
system.startActor(srijan);
|
||||
system.startActor(ansh);
|
||||
ansh.send(new Message("Hello ansh", srijan.getActorId()));
|
||||
srijan.send(new Message("Hello srijan!", ansh.getActorId()));
|
||||
|
||||
Thread.sleep(1000); // Give time for messages to process
|
||||
|
||||
srijan.stop(); // Stop the actor gracefully
|
||||
ansh.stop();
|
||||
system.shutdown(); // Stop the actor system
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## When to Use the Actor Model Pattern in Java
|
||||
|
||||
- When building **concurrent or distributed systems**
|
||||
- When you want **no shared mutable state**
|
||||
- When you need **asynchronous, message-driven communication**
|
||||
- When components should be **isolated and loosely coupled**
|
||||
|
||||
---
|
||||
|
||||
## Actor Model Pattern Java Tutorials
|
||||
|
||||
- [Baeldung – Akka with Java](https://www.baeldung.com/java-akka)
|
||||
- [Vaughn Vernon – Reactive Messaging Patterns](https://vaughnvernon.co/?p=1143)
|
||||
|
||||
---
|
||||
|
||||
## Real-World Applications of Actor Model Pattern in Java
|
||||
|
||||
- [Akka Framework](https://akka.io/)
|
||||
- [Erlang and Elixir concurrency](https://www.erlang.org/)
|
||||
- [Microsoft Orleans](https://learn.microsoft.com/en-us/dotnet/orleans/)
|
||||
- JVM-based game engines and simulators
|
||||
|
||||
---
|
||||
|
||||
## Benefits and Trade-offs of Actor Model Pattern
|
||||
|
||||
### ✅ Benefits
|
||||
- High concurrency support
|
||||
- Easy scaling across threads or machines
|
||||
- Fault isolation and recovery
|
||||
- Message ordering within actors
|
||||
|
||||
### ⚠️ Trade-offs
|
||||
- Harder to debug due to asynchronous behavior
|
||||
- Slight performance overhead due to message queues
|
||||
- More complex to design than simple method calls
|
||||
|
||||
---
|
||||
|
||||
## Related Java Design Patterns
|
||||
|
||||
- [Command Pattern](../command)
|
||||
- [Mediator Pattern](../mediator)
|
||||
- [Event-Driven Architecture](../event-driven-architecture)
|
||||
- [Observer Pattern](../observer)
|
||||
|
||||
---
|
||||
|
||||
## References and Credits
|
||||
|
||||
- *Programming Erlang*, Joe Armstrong
|
||||
- *Reactive Design Patterns*, Roland Kuhn
|
||||
- *The Actor Model in 10 Minutes*, [InfoQ Article](https://www.infoq.com/articles/actor-model/)
|
||||
- [Akka Documentation](https://doc.akka.io/docs/akka/current/index.html)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB |
@@ -1,35 +0,0 @@
|
||||
@startuml actor-model
|
||||
|
||||
title Actor Model - UML Class Diagram
|
||||
|
||||
class ActorSystem {
|
||||
+actorOf(actor: Actor): Actor
|
||||
+shutdown(): void
|
||||
}
|
||||
|
||||
class Actor {
|
||||
-mailbox: BlockingQueue<Message>
|
||||
-active: boolean
|
||||
+send(message: Message): void
|
||||
+stop(): void
|
||||
+run(): void
|
||||
#onReceive(message: Message): void
|
||||
}
|
||||
|
||||
class ExampleActor {
|
||||
+onReceive(message: Message): void
|
||||
}
|
||||
|
||||
class Message {
|
||||
-content: String
|
||||
-sender: Actor
|
||||
+getContent(): String
|
||||
+getSender(): Actor
|
||||
}
|
||||
|
||||
ActorSystem --> Actor : creates
|
||||
Actor <|-- ExampleActor : extends
|
||||
Actor --> Message : processes
|
||||
ExampleActor --> Message : uses
|
||||
|
||||
@enduml
|
||||
@@ -1,114 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
|
||||
The MIT License
|
||||
Copyright © 2014-2022 Ilkka Seppälä
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
-->
|
||||
<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
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.26.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>actor-model</artifactId>
|
||||
<name>Actor Model</name>
|
||||
|
||||
<!-- Force unified JUnit version to avoid classpath mismatches -->
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit</groupId>
|
||||
<artifactId>junit-bom</artifactId>
|
||||
<version>5.11.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-launcher</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Assembly plugin for creating fat JARs -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.iluwatar.actormodel.App</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.actormodel;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public abstract class Actor implements Runnable {
|
||||
|
||||
@Setter @Getter private String actorId;
|
||||
private final BlockingQueue<Message> mailbox = new LinkedBlockingQueue<>();
|
||||
private volatile boolean active =
|
||||
true; // always read from main memory and written back to main memory,
|
||||
|
||||
// rather than being cached in a thread's local memory. To make it consistent to all Actors
|
||||
|
||||
public void send(Message message) {
|
||||
mailbox.add(message); // Add message to queue
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
active = false; // Stop the actor loop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (active) {
|
||||
try {
|
||||
Message message = mailbox.take(); // Wait for a message
|
||||
onReceive(message); // Process it
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Child classes must define what to do with a message
|
||||
protected abstract void onReceive(Message message);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.actormodel;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ActorSystem {
|
||||
private final ExecutorService executor = Executors.newCachedThreadPool();
|
||||
private final ConcurrentHashMap<String, Actor> actorRegister = new ConcurrentHashMap<>();
|
||||
private final AtomicInteger idCounter = new AtomicInteger(0);
|
||||
|
||||
public void startActor(Actor actor) {
|
||||
String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
|
||||
actor.setActorId(actorId); // assign the actor it's ID
|
||||
actorRegister.put(actorId, actor); // Register and save the actor with it's ID
|
||||
executor.submit(actor); // Run the actor in a thread
|
||||
}
|
||||
|
||||
public Actor getActorById(String actorId) {
|
||||
return actorRegister.get(actorId); // Find by Id
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
executor.shutdownNow(); // Stop all threads
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Actor Model is a design pattern used to handle concurrency in a safe, scalable, and
|
||||
* message-driven way.
|
||||
*
|
||||
* <p>In the Actor Model: - An **Actor** is an independent unit that has its own state and behavior.
|
||||
* - Actors **communicate only through messages** — they do not share memory. - An **ActorSystem**
|
||||
* is responsible for creating, starting, and managing the lifecycle of actors. - Messages are
|
||||
* delivered asynchronously, and each actor processes them one at a time.
|
||||
*
|
||||
* <p>💡 Key benefits: - No shared memory = no need for complex thread-safety - Easy to scale with
|
||||
* many actors - Suitable for highly concurrent or distributed systems
|
||||
*
|
||||
* <p>🔍 This example demonstrates the Actor Model: - `ActorSystem` starts two actors: `srijan` and
|
||||
* `ansh`. - `ExampleActor` and `ExampleActor2` extend the `Actor` class and override the
|
||||
* `onReceive()` method to handle messages. - Actors communicate using `send()` to pass `Message`
|
||||
* objects that include the message content and sender's ID. - The actors process messages
|
||||
* **asynchronously in separate threads**, and we allow a short delay (`Thread.sleep`) to let them
|
||||
* run. - The system is shut down gracefully at the end.
|
||||
*/
|
||||
package com.iluwatar.actormodel;
|
||||
|
||||
public class App {
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
ActorSystem system = new ActorSystem();
|
||||
Actor srijan = new ExampleActor(system);
|
||||
Actor ansh = new ExampleActor2(system);
|
||||
|
||||
system.startActor(srijan);
|
||||
system.startActor(ansh);
|
||||
ansh.send(new Message("Hello ansh", srijan.getActorId()));
|
||||
srijan.send(new Message("Hello srijan!", ansh.getActorId()));
|
||||
|
||||
Thread.sleep(1000); // Give time for messages to process
|
||||
|
||||
srijan.stop(); // Stop the actor gracefully
|
||||
ansh.stop();
|
||||
system.shutdown(); // Stop the actor system
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.actormodel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class ExampleActor extends Actor {
|
||||
private final ActorSystem actorSystem;
|
||||
@Getter private final List<String> receivedMessages = new ArrayList<>();
|
||||
|
||||
public ExampleActor(ActorSystem actorSystem) {
|
||||
this.actorSystem = actorSystem;
|
||||
}
|
||||
|
||||
// Logger log = Logger.getLogger(getClass().getName());
|
||||
|
||||
@Override
|
||||
protected void onReceive(Message message) {
|
||||
LOGGER.info(
|
||||
"[{}]Received : {} from : [{}]", getActorId(), message.getContent(), message.getSenderId());
|
||||
Actor sender = actorSystem.getActorById(message.getSenderId()); // sender actor id
|
||||
// Reply of the message
|
||||
if (sender != null && !message.getSenderId().equals(getActorId())) {
|
||||
sender.send(new Message("I got your message ", getActorId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.actormodel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class ExampleActor2 extends Actor {
|
||||
private final ActorSystem actorSystem;
|
||||
@Getter private final List<String> receivedMessages = new ArrayList<>();
|
||||
|
||||
public ExampleActor2(ActorSystem actorSystem) {
|
||||
this.actorSystem = actorSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReceive(Message message) {
|
||||
receivedMessages.add(message.getContent());
|
||||
LOGGER.info("[{}]Received : {}", getActorId(), message.getContent());
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.actormodel;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class Message {
|
||||
private final String content;
|
||||
private final String senderId;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.actor;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import com.iluwatar.actormodel.ActorSystem;
|
||||
import com.iluwatar.actormodel.App;
|
||||
import com.iluwatar.actormodel.ExampleActor;
|
||||
import com.iluwatar.actormodel.ExampleActor2;
|
||||
import com.iluwatar.actormodel.Message;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ActorModelTest {
|
||||
@Test
|
||||
void testMainMethod() throws InterruptedException {
|
||||
App.main(new String[] {});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessagePassing() throws InterruptedException {
|
||||
ActorSystem system = new ActorSystem();
|
||||
|
||||
ExampleActor srijan = new ExampleActor(system);
|
||||
ExampleActor2 ansh = new ExampleActor2(system);
|
||||
|
||||
system.startActor(srijan);
|
||||
system.startActor(ansh);
|
||||
|
||||
// Ansh recieves a message from Srijan
|
||||
ansh.send(new Message("Hello ansh", srijan.getActorId()));
|
||||
|
||||
// Wait briefly to allow async processing
|
||||
Thread.sleep(200);
|
||||
|
||||
// Check that Srijan received the message
|
||||
assertTrue(
|
||||
ansh.getReceivedMessages().contains("Hello ansh"),
|
||||
"ansh should receive the message from Srijan");
|
||||
}
|
||||
}
|
||||
+74
-98
@@ -1,25 +1,22 @@
|
||||
---
|
||||
title: "Acyclic Visitor Pattern in Java: Streamlining Object Interactions"
|
||||
shortTitle: Acyclic Visitor
|
||||
description: "Learn about the Acyclic Visitor pattern in Java. This guide explains how it decouples operations from object hierarchies, providing examples and real-world applications."
|
||||
title: Acyclic Visitor
|
||||
category: Behavioral
|
||||
language: en
|
||||
tag:
|
||||
- Decoupling
|
||||
- Extensibility
|
||||
- Interface
|
||||
- Object composition
|
||||
- Extensibility
|
||||
---
|
||||
|
||||
## Intent of Acyclic Visitor Design Pattern
|
||||
## Intent
|
||||
|
||||
The Acyclic Visitor pattern in Java decouples operations from an object hierarchy, providing a flexible design for various applications.
|
||||
Allow new functions to be added to existing class hierarchies without affecting those hierarchies, and without creating
|
||||
the troublesome dependency cycles that are inherent to the GoF Visitor Pattern.
|
||||
|
||||
## Detailed Explanation of Acyclic Visitor Pattern with Real-World Examples
|
||||
## Explanation
|
||||
|
||||
Real-world example
|
||||
Real world example
|
||||
|
||||
> An analogous real-world example of the Acyclic Visitor pattern in Java is a museum guide system, demonstrating the practical application of this design pattern. Imagine a museum with various exhibits like paintings, sculptures, and historical artifacts. The museum has different types of guides (audio guide, human guide, virtual reality guide) that provide information about each exhibit. Instead of modifying the exhibits every time a new guide type is introduced, each guide implements an interface to visit different exhibit types. This way, the museum can add new types of guides without altering the existing exhibits, ensuring that the system remains extensible and maintainable without forming any dependency cycles.
|
||||
> We have a hierarchy of modem classes. The modems in this hierarchy need to be visited by an external algorithm based
|
||||
> on filtering criteria (is it Unix or DOS compatible modem).
|
||||
|
||||
In plain words
|
||||
|
||||
@@ -27,123 +24,108 @@ In plain words
|
||||
|
||||
[WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor) says
|
||||
|
||||
> The Acyclic Visitor pattern allows new functions to be added to existing class hierarchies without affecting those hierarchies, and without creating the dependency cycles that are inherent to the GangOfFour VisitorPattern.
|
||||
> The Acyclic Visitor pattern allows new functions to be added to existing class hierarchies without affecting those
|
||||
> hierarchies, and without creating the dependency cycles that are inherent to the GangOfFour VisitorPattern.
|
||||
|
||||
Sequence diagram
|
||||
|
||||

|
||||
|
||||
|
||||
## Programmatic Example of Acyclic Visitor in Java
|
||||
|
||||
In this Java example, we have a hierarchy of modem classes illustrating the Acyclic Visitor pattern. The modems in this hierarchy need to be visited by an external algorithm based on filtering criteria (is it Unix or DOS compatible modem).
|
||||
**Programmatic Example**
|
||||
|
||||
Here's the `Modem` hierarchy.
|
||||
|
||||
```java
|
||||
public abstract class Modem {
|
||||
public abstract void accept(ModemVisitor modemVisitor);
|
||||
public abstract void accept(ModemVisitor modemVisitor);
|
||||
}
|
||||
|
||||
public class Zoom extends Modem {
|
||||
|
||||
// Other properties and methods...
|
||||
|
||||
@Override
|
||||
public void accept(ModemVisitor modemVisitor) {
|
||||
if (modemVisitor instanceof ZoomVisitor) {
|
||||
((ZoomVisitor) modemVisitor).visit(this);
|
||||
} else {
|
||||
LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
|
||||
}
|
||||
...
|
||||
@Override
|
||||
public void accept(ModemVisitor modemVisitor) {
|
||||
if (modemVisitor instanceof ZoomVisitor) {
|
||||
((ZoomVisitor) modemVisitor).visit(this);
|
||||
} else {
|
||||
LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Hayes extends Modem {
|
||||
|
||||
// Other properties and methods...
|
||||
|
||||
@Override
|
||||
public void accept(ModemVisitor modemVisitor) {
|
||||
if (modemVisitor instanceof HayesVisitor) {
|
||||
((HayesVisitor) modemVisitor).visit(this);
|
||||
} else {
|
||||
LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
|
||||
}
|
||||
...
|
||||
@Override
|
||||
public void accept(ModemVisitor modemVisitor) {
|
||||
if (modemVisitor instanceof HayesVisitor) {
|
||||
((HayesVisitor) modemVisitor).visit(this);
|
||||
} else {
|
||||
LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next, we introduce the `ModemVisitor` hierarchy.
|
||||
Next we introduce the `ModemVisitor` hierarchy.
|
||||
|
||||
```java
|
||||
public interface ModemVisitor {
|
||||
}
|
||||
|
||||
public interface HayesVisitor extends ModemVisitor {
|
||||
void visit(Hayes hayes);
|
||||
void visit(Hayes hayes);
|
||||
}
|
||||
|
||||
public interface ZoomVisitor extends ModemVisitor {
|
||||
void visit(Zoom zoom);
|
||||
void visit(Zoom zoom);
|
||||
}
|
||||
|
||||
public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
|
||||
}
|
||||
|
||||
public class ConfigureForDosVisitor implements AllModemVisitor {
|
||||
|
||||
// Other properties and methods...
|
||||
|
||||
@Override
|
||||
public void visit(Hayes hayes) {
|
||||
LOGGER.info(hayes + " used with Dos configurator.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Zoom zoom) {
|
||||
LOGGER.info(zoom + " used with Dos configurator.");
|
||||
}
|
||||
...
|
||||
@Override
|
||||
public void visit(Hayes hayes) {
|
||||
LOGGER.info(hayes + " used with Dos configurator.");
|
||||
}
|
||||
@Override
|
||||
public void visit(Zoom zoom) {
|
||||
LOGGER.info(zoom + " used with Dos configurator.");
|
||||
}
|
||||
}
|
||||
|
||||
public class ConfigureForUnixVisitor implements ZoomVisitor {
|
||||
|
||||
// Other properties and methods...
|
||||
|
||||
@Override
|
||||
public void visit(Zoom zoom) {
|
||||
LOGGER.info(zoom + " used with Unix configurator.");
|
||||
}
|
||||
...
|
||||
@Override
|
||||
public void visit(Zoom zoom) {
|
||||
LOGGER.info(zoom + " used with Unix configurator.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Finally, here are the visitors in action.
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
var conUnix = new ConfigureForUnixVisitor();
|
||||
var conDos = new ConfigureForDosVisitor();
|
||||
|
||||
var zoom = new Zoom();
|
||||
var hayes = new Hayes();
|
||||
|
||||
hayes.accept(conDos); // Hayes modem with Dos configurator
|
||||
zoom.accept(conDos); // Zoom modem with Dos configurator
|
||||
hayes.accept(conUnix); // Hayes modem with Unix configurator
|
||||
zoom.accept(conUnix); // Zoom modem with Unix configurator
|
||||
}
|
||||
hayes.accept(conDos);
|
||||
zoom.accept(conDos);
|
||||
hayes.accept(conUnix);
|
||||
zoom.accept(conUnix);
|
||||
```
|
||||
|
||||
Program output:
|
||||
|
||||
```
|
||||
09:15:11.125 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForDosVisitor -- Hayes modem used with Dos configurator.
|
||||
09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForDosVisitor -- Zoom modem used with Dos configurator.
|
||||
09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.Hayes -- Only HayesVisitor is allowed to visit Hayes modem
|
||||
09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForUnixVisitor -- Zoom modem used with Unix configurator.
|
||||
// Hayes modem used with Dos configurator.
|
||||
// Zoom modem used with Dos configurator.
|
||||
// Only HayesVisitor is allowed to visit Hayes modem
|
||||
// Zoom modem used with Unix configurator.
|
||||
```
|
||||
|
||||
## When to Use the Acyclic Visitor Pattern in Java
|
||||
## Class diagram
|
||||
|
||||

|
||||
|
||||
## Applicability
|
||||
|
||||
This pattern can be used:
|
||||
|
||||
@@ -153,34 +135,28 @@ This pattern can be used:
|
||||
* When the visited class hierarchy will be frequently extended with new derivatives of the Element class.
|
||||
* When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.
|
||||
|
||||
## Acyclic Visitor Pattern Java Tutorials
|
||||
## Tutorial
|
||||
|
||||
* [The Acyclic Visitor Pattern (Code Crafter)](https://codecrafter.blogspot.com/2012/12/the-acyclic-visitor-pattern.html)
|
||||
* [Acyclic Visitor Pattern Example](https://codecrafter.blogspot.com/2012/12/the-acyclic-visitor-pattern.html)
|
||||
|
||||
## Benefits and Trade-offs of Acyclic Visitor Pattern
|
||||
## Consequences
|
||||
|
||||
Benefits:
|
||||
The good:
|
||||
|
||||
* Extensible: New operations can be added easily without changing the object structure.
|
||||
* Decoupled: Reduces coupling between the objects and the operations on them.
|
||||
* No dependency cycles: Ensures acyclic dependencies, improving maintainability and reducing complexity.
|
||||
* No dependency cycles between class hierarchies.
|
||||
* No need to recompile all the visitors if a new one is added.
|
||||
* Does not cause compilation failure in existing visitors if class hierarchy has a new member.
|
||||
|
||||
Trade-offs:
|
||||
The bad:
|
||||
|
||||
* Increased complexity: Can introduce additional complexity with the need for multiple visitor interfaces.
|
||||
* Maintenance overhead: Modifying the object hierarchy requires updating all visitors.
|
||||
* Violates [Liskov's Substitution Principle](https://java-design-patterns.com/principles/#liskov-substitution-principle) by showing that it can accept all visitors but actually only being interested in particular visitors.
|
||||
* Parallel hierarchy of visitors has to be created for all members in visitable class hierarchy.
|
||||
|
||||
## Related Java Design Patterns
|
||||
## Related patterns
|
||||
|
||||
* [Composite](https://java-design-patterns.com/patterns/composite/): Often used in conjunction with Acyclic Visitor to allow treating individual objects and compositions uniformly.
|
||||
* [Decorator](https://java-design-patterns.com/patterns/decorator/): Can be used alongside to add responsibilities to objects dynamically.
|
||||
* [Visitor](https://java-design-patterns.com/patterns/visitor/): The Acyclic Visitor pattern is a variation of the Visitor pattern that avoids cyclic dependencies.
|
||||
* [Visitor Pattern](https://java-design-patterns.com/patterns/visitor/)
|
||||
|
||||
## References and Credits
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
|
||||
* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
|
||||
* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
|
||||
* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML](https://amzn.to/4bOtzwF)
|
||||
* [Acyclic Visitor (Robert C. Martin)](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
|
||||
* [Acyclic Visitor (WikiWikiWeb)](https://wiki.c2.com/?AcyclicVisitor)
|
||||
* [Acyclic Visitor by Robert C. Martin](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
|
||||
* [Acyclic Visitor in WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB |
@@ -34,14 +34,6 @@
|
||||
</parent>
|
||||
<artifactId>acyclic-visitor</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
|
||||
@@ -28,4 +28,6 @@ package com.iluwatar.acyclicvisitor;
|
||||
* All ModemVisitor interface extends all visitor interfaces. This interface provides ease of use
|
||||
* when a visitor needs to visit all modem types.
|
||||
*/
|
||||
public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {}
|
||||
public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
|
||||
|
||||
}
|
||||
@@ -37,7 +37,9 @@ package com.iluwatar.acyclicvisitor;
|
||||
*/
|
||||
public class App {
|
||||
|
||||
/** Program's entry point. */
|
||||
/**
|
||||
* Program's entry point.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
var conUnix = new ConfigureForUnixVisitor();
|
||||
var conDos = new ConfigureForDosVisitor();
|
||||
@@ -48,6 +50,6 @@ public class App {
|
||||
hayes.accept(conDos); // Hayes modem with Dos configurator
|
||||
zoom.accept(conDos); // Zoom modem with Dos configurator
|
||||
hayes.accept(conUnix); // Hayes modem with Unix configurator
|
||||
zoom.accept(conUnix); // Zoom modem with Unix configurator
|
||||
zoom.accept(conUnix); // Zoom modem with Unix configurator
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -27,7 +27,8 @@ package com.iluwatar.acyclicvisitor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos manufacturer.
|
||||
* ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos
|
||||
* manufacturer.
|
||||
*/
|
||||
@Slf4j
|
||||
public class ConfigureForDosVisitor implements AllModemVisitor {
|
||||
|
||||
+1
-1
@@ -37,4 +37,4 @@ public class ConfigureForUnixVisitor implements ZoomVisitor {
|
||||
public void visit(Zoom zoom) {
|
||||
LOGGER.info(zoom + " used with Unix configurator.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,15 @@ package com.iluwatar.acyclicvisitor;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/** Hayes class implements its accept method. */
|
||||
/**
|
||||
* Hayes class implements its accept method.
|
||||
*/
|
||||
@Slf4j
|
||||
public class Hayes implements Modem {
|
||||
|
||||
/** Accepts all visitors but honors only HayesVisitor. */
|
||||
/**
|
||||
* Accepts all visitors but honors only HayesVisitor.
|
||||
*/
|
||||
@Override
|
||||
public void accept(ModemVisitor modemVisitor) {
|
||||
if (modemVisitor instanceof HayesVisitor) {
|
||||
@@ -38,9 +42,12 @@ public class Hayes implements Modem {
|
||||
} else {
|
||||
LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Hayes' modem's toString method. */
|
||||
/**
|
||||
* Hayes' modem's toString method.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Hayes modem";
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.acyclicvisitor;
|
||||
|
||||
/** HayesVisitor interface. */
|
||||
/**
|
||||
* HayesVisitor interface.
|
||||
*/
|
||||
public interface HayesVisitor extends ModemVisitor {
|
||||
void visit(Hayes hayes);
|
||||
}
|
||||
@@ -24,7 +24,10 @@
|
||||
*/
|
||||
package com.iluwatar.acyclicvisitor;
|
||||
|
||||
/** //Modem abstract class. converted to an interface */
|
||||
/**
|
||||
* //Modem abstract class.
|
||||
* converted to an interface
|
||||
*/
|
||||
public interface Modem {
|
||||
void accept(ModemVisitor modemVisitor);
|
||||
}
|
||||
@@ -26,11 +26,15 @@ package com.iluwatar.acyclicvisitor;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/** Zoom class implements its accept method. */
|
||||
/**
|
||||
* Zoom class implements its accept method.
|
||||
*/
|
||||
@Slf4j
|
||||
public class Zoom implements Modem {
|
||||
|
||||
/** Accepts all visitors but honors only ZoomVisitor. */
|
||||
/**
|
||||
* Accepts all visitors but honors only ZoomVisitor.
|
||||
*/
|
||||
@Override
|
||||
public void accept(ModemVisitor modemVisitor) {
|
||||
if (modemVisitor instanceof ZoomVisitor) {
|
||||
@@ -40,7 +44,9 @@ public class Zoom implements Modem {
|
||||
}
|
||||
}
|
||||
|
||||
/** Zoom modem's toString method. */
|
||||
/**
|
||||
* Zoom modem's toString method.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Zoom modem";
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
package com.iluwatar.acyclicvisitor;
|
||||
|
||||
/** ZoomVisitor interface. */
|
||||
/**
|
||||
* ZoomVisitor interface.
|
||||
*/
|
||||
public interface ZoomVisitor extends ModemVisitor {
|
||||
void visit(Zoom zoom);
|
||||
}
|
||||
@@ -24,22 +24,25 @@
|
||||
*/
|
||||
package com.iluwatar.acyclicvisitor;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests that the Acyclic Visitor example runs without errors. */
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
/**
|
||||
* Tests that the Acyclic Visitor example runs without errors.
|
||||
*/
|
||||
class AppTest {
|
||||
|
||||
/**
|
||||
* Issue: Add at least one assertion to this test case.
|
||||
*
|
||||
* <p>Solution: Inserted assertion to check whether the execution of the main method in {@link
|
||||
* App} throws an exception.
|
||||
* Solution: Inserted assertion to check whether the execution of the main method in {@link App}
|
||||
* throws an exception.
|
||||
*/
|
||||
|
||||
@Test
|
||||
void shouldExecuteApplicationWithoutException() {
|
||||
|
||||
assertDoesNotThrow(() -> App.main(new String[] {}));
|
||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,14 @@
|
||||
*/
|
||||
package com.iluwatar.acyclicvisitor;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Hayes test class */
|
||||
/**
|
||||
* Hayes test class
|
||||
*/
|
||||
class HayesTest {
|
||||
|
||||
@Test
|
||||
|
||||
@@ -24,13 +24,16 @@
|
||||
*/
|
||||
package com.iluwatar.acyclicvisitor;
|
||||
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Zoom test class */
|
||||
/**
|
||||
* Zoom test class
|
||||
*/
|
||||
class ZoomTest {
|
||||
|
||||
@Test
|
||||
|
||||
+64
-79
@@ -1,31 +1,25 @@
|
||||
---
|
||||
title: "Adapter Pattern in Java: Seamless Integration of Incompatible Systems"
|
||||
shortTitle: Adapter
|
||||
description: "Learn how the Adapter Design Pattern works in Java with detailed examples and use cases. Understand how it enables compatibility between incompatible interfaces."
|
||||
title: Adapter
|
||||
category: Structural
|
||||
language: en
|
||||
tag:
|
||||
- Compatibility
|
||||
- Decoupling
|
||||
- Gang of Four
|
||||
- Interface
|
||||
- Object composition
|
||||
- Wrapping
|
||||
- Gang of Four
|
||||
---
|
||||
|
||||
## Also known as
|
||||
Wrapper
|
||||
|
||||
* Wrapper
|
||||
## Intent
|
||||
Convert the interface of a class into another interface the clients expect. Adapter lets classes work together that
|
||||
couldn't otherwise because of incompatible interfaces.
|
||||
|
||||
## Intent of Adapter Design Pattern
|
||||
|
||||
The Adapter Design Pattern in Java converts the interface of a class into another interface that clients expect, enabling compatibility.
|
||||
|
||||
## Detailed Explanation of Adapter Pattern with Real-World Examples
|
||||
## Explanation
|
||||
|
||||
Real-world example
|
||||
|
||||
> Consider that you have some pictures on your memory card and you need to transfer them to your computer. To transfer them, you need some kind of adapter that is compatible with your computer ports so that you can attach a memory card to your computer. In this case card reader is an adapter. Another example would be the famous power adapter; a three-legged plug can't be connected to a two-pronged outlet, it needs to use a power adapter that makes it compatible with the two-pronged outlets. Yet another example would be a translator translating words spoken by one person to another
|
||||
> Consider that you have some pictures on your memory card and you need to transfer them to your computer. To transfer them, you need some kind of adapter that is compatible with your computer ports so that you can attach a memory card to your computer. In this case card reader is an adapter.
|
||||
> Another example would be the famous power adapter; a three-legged plug can't be connected to a two-pronged outlet, it needs to use a power adapter that makes it compatible with the two-pronged outlets.
|
||||
> Yet another example would be a translator translating words spoken by one person to another
|
||||
|
||||
In plain words
|
||||
|
||||
@@ -35,120 +29,111 @@ Wikipedia says
|
||||
|
||||
> In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.
|
||||
|
||||
Sequence diagram
|
||||
**Programmatic Example**
|
||||
|
||||

|
||||
|
||||
## Programmatic Example of Adapter Pattern in Java
|
||||
|
||||
The Adapter Pattern example in Java shows how a class with an incompatible interface can be adapted to work with another class.
|
||||
|
||||
Consider a wannabe captain that can only use rowing boats but can't sail at all.
|
||||
Consider a captain that can only use rowing boats and cannot sail at all.
|
||||
|
||||
First, we have interfaces `RowingBoat` and `FishingBoat`
|
||||
|
||||
```java
|
||||
public interface RowingBoat {
|
||||
void row();
|
||||
void row();
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class FishingBoat {
|
||||
public void sail() {
|
||||
LOGGER.info("The fishing boat is sailing");
|
||||
}
|
||||
public void sail() {
|
||||
LOGGER.info("The fishing boat is sailing");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The captain expects an implementation of `RowingBoat` interface to be able to move.
|
||||
And captain expects an implementation of `RowingBoat` interface to be able to move
|
||||
|
||||
```java
|
||||
public class Captain {
|
||||
|
||||
private final RowingBoat rowingBoat;
|
||||
private final RowingBoat rowingBoat;
|
||||
// default constructor and setter for rowingBoat
|
||||
public Captain(RowingBoat rowingBoat) {
|
||||
this.rowingBoat = rowingBoat;
|
||||
}
|
||||
|
||||
// default constructor and setter for rowingBoat
|
||||
public Captain(RowingBoat rowingBoat) {
|
||||
this.rowingBoat = rowingBoat;
|
||||
}
|
||||
|
||||
public void row() {
|
||||
rowingBoat.row();
|
||||
}
|
||||
public void row() {
|
||||
rowingBoat.row();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, let's say the pirates are coming and our captain needs to escape but there is only a fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills.
|
||||
Now let's say the pirates are coming and our captain needs to escape but there is only a fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills.
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class FishingBoatAdapter implements RowingBoat {
|
||||
|
||||
private final FishingBoat boat;
|
||||
private final FishingBoat boat;
|
||||
|
||||
public FishingBoatAdapter() {
|
||||
boat = new FishingBoat();
|
||||
}
|
||||
public FishingBoatAdapter() {
|
||||
boat = new FishingBoat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void row() {
|
||||
boat.sail();
|
||||
}
|
||||
@Override
|
||||
public void row() {
|
||||
boat.sail();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now the `Captain` can use the `FishingBoat` to escape the pirates.
|
||||
And now the `Captain` can use the `FishingBoat` to escape the pirates.
|
||||
|
||||
```java
|
||||
public static void main(final String[] args) {
|
||||
// The captain can only operate rowing boats but with adapter he is able to
|
||||
// use fishing boats as well
|
||||
var captain = new Captain(new FishingBoatAdapter());
|
||||
captain.row();
|
||||
}
|
||||
var captain = new Captain(new FishingBoatAdapter());
|
||||
captain.row();
|
||||
```
|
||||
|
||||
The program outputs:
|
||||
## Class diagram
|
||||

|
||||
|
||||
```
|
||||
10:25:08.074 [main] INFO com.iluwatar.adapter.FishingBoat -- The fishing boat is sailing
|
||||
```
|
||||
|
||||
## When to Use the Adapter Pattern in Java
|
||||
|
||||
Use the Adapter pattern in Java when
|
||||
## Applicability
|
||||
Use the Adapter pattern when
|
||||
|
||||
* You want to use an existing class, and its interface does not match the one you need
|
||||
* You want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces
|
||||
* You need to use several existing subclasses, but it's impractical to adapt their interface by subclassing everyone. An object adapter can adapt the interface of its parent class.
|
||||
* Most of the applications using third-party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
|
||||
|
||||
## Adapter Pattern Java Tutorials
|
||||
## Tutorials
|
||||
|
||||
* [Using the Adapter Design Pattern in Java (Dzone)](https://dzone.com/articles/adapter-design-pattern-in-java)
|
||||
* [Adapter in Java (Refactoring Guru)](https://refactoring.guru/design-patterns/adapter/java/example)
|
||||
* [The Adapter Pattern in Java (Baeldung)](https://www.baeldung.com/java-adapter-pattern)
|
||||
* [Adapter Design Pattern (GeeksForGeeks)](https://www.geeksforgeeks.org/adapter-pattern/)
|
||||
* [Dzone](https://dzone.com/articles/adapter-design-pattern-in-java)
|
||||
* [Refactoring Guru](https://refactoring.guru/design-patterns/adapter/java/example)
|
||||
* [Baeldung](https://www.baeldung.com/java-adapter-pattern)
|
||||
* [GeeksforGeeks](https://www.geeksforgeeks.org/adapter-pattern/)
|
||||
|
||||
## Benefits and Trade-offs of Adapter Pattern
|
||||
|
||||
Class and object adapters offer different benefits and drawbacks. A class adapter adapts the Adaptee to the Target by binding to a specific Adaptee class, which means it cannot adapt a class and all its subclasses. This type of adapter allows the Adapter to override some of the Adaptee’s behavior because the Adapter is a subclass of the Adaptee. Additionally, it introduces only one object without needing extra pointer indirection to reach the Adaptee.
|
||||
## Consequences
|
||||
Class and object adapters have different trade-offs. A class adapter
|
||||
|
||||
On the other hand, an object adapter allows a single Adapter to work with multiple Adaptees, including the Adaptee and all its subclasses. This type of adapter can add functionality to all Adaptees simultaneously. However, it makes overriding the Adaptee’s behavior more difficult, as it requires subclassing the Adaptee and having the Adapter refer to this subclass instead of the Adaptee itself.
|
||||
* Adapts Adaptee to Target by committing to a concrete Adaptee class. As a consequence, a class adapter won’t work when we want to adapt a class and all its subclasses.
|
||||
* Lets Adapter override some of Adaptee’s behavior since Adapter is a subclass of Adaptee.
|
||||
* Introduces only one object, and no additional pointer indirection is needed to get to the adaptee.
|
||||
|
||||
## Real-World Applications of Adapter Pattern in Java
|
||||
An object adapter
|
||||
|
||||
* Lets a single Adapter work with many Adaptees, that is, the Adaptee itself and all of its subclasses (if any). The Adapter can also add functionality to all Adaptees at once.
|
||||
* Makes it harder to override Adaptee behavior. It will require subclassing Adaptee and making the Adapter refer to the subclass rather than the Adaptee itself.
|
||||
|
||||
|
||||
## Real-world examples
|
||||
|
||||
* `java.io.InputStreamReader` and `java.io.OutputStreamWriter` in the Java IO library.
|
||||
* GUI component libraries that allow for plug-ins or adapters to convert between different GUI component interfaces.
|
||||
* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
|
||||
* [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-)
|
||||
* [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-)
|
||||
* [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-)
|
||||
|
||||
## References and Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
|
||||
* [Effective Java](https://amzn.to/4cGk2Jz)
|
||||
* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
|
||||
* [J2EE Design Patterns](https://amzn.to/4dpzgmx)
|
||||
* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 27 KiB |
+1
-9
@@ -4,7 +4,7 @@
|
||||
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
|
||||
The MIT License
|
||||
Copyright © 2014-2022 Ilkka Seppälä
|
||||
Copyright © 2014-2023 Ilkka Seppälä
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -34,14 +34,6 @@
|
||||
</parent>
|
||||
<artifactId>adapter</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
|
||||
@@ -37,15 +37,16 @@ package com.iluwatar.adapter;
|
||||
* <p>The Adapter ({@link FishingBoatAdapter}) converts the interface of the adaptee class ({@link
|
||||
* FishingBoat}) into a suitable one expected by the client ({@link RowingBoat}).
|
||||
*
|
||||
* <p>The story of this implementation is this. <br>
|
||||
* Pirates are coming! we need a {@link RowingBoat} to flee! We have a {@link FishingBoat} and our
|
||||
* captain. We have no time to make up a new ship! we need to reuse this {@link FishingBoat}. The
|
||||
* captain needs a rowing boat which he can operate. The spec is in {@link RowingBoat}. We will use
|
||||
* the Adapter pattern to reuse {@link FishingBoat}.
|
||||
* <p>The story of this implementation is this. <br> Pirates are coming! we need a {@link
|
||||
* RowingBoat} to flee! We have a {@link FishingBoat} and our captain. We have no time to make up a
|
||||
* new ship! we need to reuse this {@link FishingBoat}. The captain needs a rowing boat which he can
|
||||
* operate. The spec is in {@link RowingBoat}. We will use the Adapter pattern to reuse {@link
|
||||
* FishingBoat}.
|
||||
*/
|
||||
public final class App {
|
||||
|
||||
private App() {}
|
||||
private App() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Program entry point.
|
||||
|
||||
@@ -29,8 +29,7 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* The Captain uses {@link RowingBoat} to sail. <br>
|
||||
* This is the client in the pattern.
|
||||
* The Captain uses {@link RowingBoat} to sail. <br> This is the client in the pattern.
|
||||
*/
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@@ -42,4 +41,5 @@ public final class Captain {
|
||||
void row() {
|
||||
rowingBoat.row();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,4 +36,5 @@ final class FishingBoat {
|
||||
void sail() {
|
||||
LOGGER.info("The fishing boat is sailing");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,10 +25,10 @@
|
||||
package com.iluwatar.adapter;
|
||||
|
||||
/**
|
||||
* The interface expected by the client.<br>
|
||||
* A rowing boat is rowed to move.
|
||||
* The interface expected by the client.<br> A rowing boat is rowed to move.
|
||||
*/
|
||||
public interface RowingBoat {
|
||||
|
||||
void row();
|
||||
|
||||
}
|
||||
@@ -24,15 +24,17 @@
|
||||
*/
|
||||
package com.iluwatar.adapter;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for the adapter pattern. */
|
||||
/**
|
||||
* Tests for the adapter pattern.
|
||||
*/
|
||||
class AdapterPatternTest {
|
||||
|
||||
private Map<String, Object> beans;
|
||||
@@ -41,7 +43,9 @@ class AdapterPatternTest {
|
||||
|
||||
private static final String ROWING_BEAN = "captain";
|
||||
|
||||
/** This method runs before the test execution and sets the bean objects in the beans Map. */
|
||||
/**
|
||||
* This method runs before the test execution and sets the bean objects in the beans Map.
|
||||
*/
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
beans = new HashMap<>();
|
||||
|
||||
@@ -24,17 +24,23 @@
|
||||
*/
|
||||
package com.iluwatar.adapter;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests that Adapter example runs without errors. */
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
/**
|
||||
* Tests that Adapter example runs without errors.
|
||||
*/
|
||||
class AppTest {
|
||||
|
||||
/** Check whether the execution of the main method in {@link App} throws an exception. */
|
||||
/**
|
||||
* Check whether the execution of the main method in {@link App}
|
||||
* throws an exception.
|
||||
*/
|
||||
|
||||
@Test
|
||||
void shouldExecuteApplicationWithoutException() {
|
||||
|
||||
assertDoesNotThrow(() -> App.main(new String[] {}));
|
||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
||||
}
|
||||
}
|
||||
+23
-22
@@ -1,36 +1,36 @@
|
||||
---
|
||||
title: Aggregator Microservices
|
||||
category: Architectural
|
||||
language: fr
|
||||
language: en
|
||||
tag:
|
||||
- Cloud distributed
|
||||
- Decoupling
|
||||
- Microservices
|
||||
---
|
||||
|
||||
## Intention
|
||||
## Intent
|
||||
|
||||
L'utilisateur fait un appel unique au service d'agrégation, et ce dernier appelle ensuite chaque microservice approprié.
|
||||
The user makes a single call to the aggregator service, and the aggregator then calls each relevant microservice.
|
||||
|
||||
## Explication
|
||||
## Explanation
|
||||
|
||||
Exemple concret
|
||||
Real world example
|
||||
|
||||
> Notre marché en ligne a besoin d'informations sur les produits et leur stock actuel. Elle fait appel à un service
|
||||
> d'agrégation qui, à son tour, appelle le microservice d'information sur les produits et le microservice d'inventaire
|
||||
> des produits et renvoie les informations combinées.
|
||||
> Our web marketplace needs information about products and their current inventory. It makes a call to an aggregator
|
||||
> service which in turn calls the product information microservice and product inventory microservice returning the
|
||||
> combined information.
|
||||
|
||||
En clair
|
||||
In plain words
|
||||
|
||||
> Aggregator Microservice collecte des éléments de données provenant de divers microservices et renvoie un agrégat pour traitement..
|
||||
> Aggregator Microservice collects pieces of data from various microservices and returns an aggregate for processing.
|
||||
|
||||
Stack Overflow dit
|
||||
Stack Overflow says
|
||||
|
||||
> Aggregator Microservice invoque plusieurs services pour réaliser la fonctionnalité requise par l'application.
|
||||
> Aggregator Microservice invokes multiple services to achieve the functionality required by the application.
|
||||
|
||||
**Exemple de programme**
|
||||
**Programmatic Example**
|
||||
|
||||
Commençons par le modèle de données. Voici notre `Product`.
|
||||
Let's start from the data model. Here's our `Product`.
|
||||
|
||||
```java
|
||||
public class Product {
|
||||
@@ -41,8 +41,8 @@ public class Product {
|
||||
}
|
||||
```
|
||||
|
||||
Ensuite, nous pouvons présenter notre microservice `Aggregator`. Il contient les clients `ProductInformationClient` et
|
||||
`ProductInventoryClient` pour appeler les microservices respectifs.
|
||||
Next we can introduce our `Aggregator` microservice. It contains clients `ProductInformationClient` and
|
||||
`ProductInventoryClient` for calling respective microservices.
|
||||
|
||||
```java
|
||||
@RestController
|
||||
@@ -72,7 +72,8 @@ public class Aggregator {
|
||||
}
|
||||
```
|
||||
|
||||
Voici l'essentiel de la mise en œuvre du microservice d'information. Le microservice dInventory est similaire, il renvoie simplement l'inventaire.
|
||||
Here's the essence of information microservice implementation. Inventory microservice is similar, it just returns
|
||||
inventory counts.
|
||||
|
||||
```java
|
||||
@RestController
|
||||
@@ -84,7 +85,7 @@ public class InformationController {
|
||||
}
|
||||
```
|
||||
|
||||
L'appel à notre API REST `Aggregator` renvoie les informations sur le produit.
|
||||
Now calling our `Aggregator` REST API returns the product information.
|
||||
|
||||
```bash
|
||||
curl http://localhost:50004/product
|
||||
@@ -93,13 +94,13 @@ curl http://localhost:50004/product
|
||||
|
||||
## Class diagram
|
||||
|
||||

|
||||

|
||||
|
||||
## Application
|
||||
## Applicability
|
||||
|
||||
Utilisez le modèle de microservices agrégateur lorsque vous avez besoin d'une API unifiée pour différents microservices, quel que soit l'appareil client.
|
||||
Use the Aggregator Microservices pattern when you need a unified API for various microservices, regardless the client device.
|
||||
|
||||
## Crédits
|
||||
## Credits
|
||||
|
||||
* [Microservice Design Patterns](http://web.archive.org/web/20190705163602/http://blog.arungupta.me/microservice-design-patterns/)
|
||||
* [Microservices Patterns: With examples in Java](https://www.amazon.com/gp/product/1617294543/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617294543&linkId=8b4e570267bc5fb8b8189917b461dc60)
|
||||
File renamed without changes.
File renamed without changes.
+1
-2
@@ -27,7 +27,7 @@
|
||||
-->
|
||||
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>microservices-aggregrator</artifactId>
|
||||
<artifactId>aggregator-microservices</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.26.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
@@ -38,7 +38,6 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<version>6.2.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
+8
-5
@@ -26,7 +26,7 @@ package com.iluwatar.aggregator.microservices;
|
||||
|
||||
import static java.util.Objects.requireNonNullElse;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@@ -37,9 +37,11 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RestController
|
||||
public class Aggregator {
|
||||
|
||||
@Resource private ProductInformationClient informationClient;
|
||||
@Resource
|
||||
private ProductInformationClient informationClient;
|
||||
|
||||
@Resource private ProductInventoryClient inventoryClient;
|
||||
@Resource
|
||||
private ProductInventoryClient inventoryClient;
|
||||
|
||||
/**
|
||||
* Retrieves product data.
|
||||
@@ -53,12 +55,13 @@ public class Aggregator {
|
||||
var productTitle = informationClient.getProductTitle();
|
||||
var productInventory = inventoryClient.getProductInventories();
|
||||
|
||||
// Fallback to error message
|
||||
//Fallback to error message
|
||||
product.setTitle(requireNonNullElse(productTitle, "Error: Fetching Product Title Failed"));
|
||||
|
||||
// Fallback to default error inventory
|
||||
//Fallback to default error inventory
|
||||
product.setProductInventories(requireNonNullElse(productInventory, -1));
|
||||
|
||||
return product;
|
||||
}
|
||||
|
||||
}
|
||||
+3
-1
@@ -27,7 +27,9 @@ package com.iluwatar.aggregator.microservices;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/** Spring Boot EntryPoint Class. */
|
||||
/**
|
||||
* Spring Boot EntryPoint Class.
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class App {
|
||||
|
||||
+11
-3
@@ -27,14 +27,22 @@ package com.iluwatar.aggregator.microservices;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/** Encapsulates all the data for a Product that clients will request. */
|
||||
/**
|
||||
* Encapsulates all the data for a Product that clients will request.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class Product {
|
||||
|
||||
/** The title of the product. */
|
||||
/**
|
||||
* The title of the product.
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/** The inventories of the product. */
|
||||
|
||||
/**
|
||||
* The inventories of the product.
|
||||
*/
|
||||
private int productInventories;
|
||||
|
||||
}
|
||||
+4
-1
@@ -24,8 +24,11 @@
|
||||
*/
|
||||
package com.iluwatar.aggregator.microservices;
|
||||
|
||||
/** Interface for the Information micro-service. */
|
||||
/**
|
||||
* Interface for the Information micro-service.
|
||||
*/
|
||||
public interface ProductInformationClient {
|
||||
|
||||
String getProductTitle();
|
||||
|
||||
}
|
||||
Loaded 100 of 3288 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user