Skip to content

Commit 766d14a

Browse files
committed
binding property implemented
1 parent adbddcb commit 766d14a

File tree

10 files changed

+315
-0
lines changed

10 files changed

+315
-0
lines changed

binding-property/etc/binding.puml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
@startuml
2+
title Two-Way Binding Properties Pattern - Class Diagram
3+
4+
class ObservableProperty {
5+
- value : T
6+
- observers : List<Observer<T>>
7+
- isUpdating : boolean
8+
+ ObservableProperty(initialValue : T)
9+
+ getValue() : T
10+
+ setValue(value : T)
11+
+ addObserver(observer : Observer<T>)
12+
+ removeObserver(observer : Observer<T>)
13+
- notifyObservers()
14+
}
15+
16+
interface Observer {
17+
+ update(newValue : T)
18+
+ bind(observableProperty : ObservableProperty<T>)
19+
+ unbind()
20+
}
21+
22+
class TextView {
23+
- text : String
24+
- observableProperty : ObservableProperty<String>
25+
+ update(newValue : String)
26+
+ bind(observableProperty : ObservableProperty<String>)
27+
+ unbind()
28+
+ setText(newText : String)
29+
+ getText() : String
30+
}
31+
32+
ObservableProperty "1" o-- "*" Observer
33+
Observer <|-- TextView
34+
35+
@enduml

binding-property/pom.xml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
5+
6+
The MIT License
7+
Copyright © 2014-2022 Ilkka Seppälä
8+
9+
Permission is hereby granted, free of charge, to any person obtaining a copy
10+
of this software and associated documentation files (the "Software"), to deal
11+
in the Software without restriction, including without limitation the rights
12+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
copies of the Software, and to permit persons to whom the Software is
14+
furnished to do so, subject to the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included in
17+
all copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.
26+
27+
-->
28+
<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">
29+
<modelVersion>4.0.0</modelVersion>
30+
<parent>
31+
<groupId>com.iluwatar</groupId>
32+
<artifactId>java-design-patterns</artifactId>
33+
<version>1.26.0-SNAPSHOT</version>
34+
</parent>
35+
<artifactId>bridge</artifactId>
36+
<dependencies>
37+
<dependency>
38+
<groupId>org.junit.jupiter</groupId>
39+
<artifactId>junit-jupiter-engine</artifactId>
40+
<scope>test</scope>
41+
</dependency>
42+
<dependency>
43+
<groupId>org.mockito</groupId>
44+
<artifactId>mockito-core</artifactId>
45+
<scope>test</scope>
46+
</dependency>
47+
</dependencies>
48+
<build>
49+
<plugins>
50+
<plugin>
51+
<groupId>org.apache.maven.plugins</groupId>
52+
<artifactId>maven-assembly-plugin</artifactId>
53+
<executions>
54+
<execution>
55+
<configuration>
56+
<archive>
57+
<manifest>
58+
<mainClass>com.iluwatar.binding.App</mainClass>
59+
</manifest>
60+
</archive>
61+
</configuration>
62+
</execution>
63+
</executions>
64+
</plugin>
65+
</plugins>
66+
</build>
67+
</project>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.iluwatar.binding;
2+
3+
public class App {
4+
public static void main(String[] args) {
5+
ObservableProperty<String> observableProperty = new ObservableProperty<>("Initial Value");
6+
TextView textView = new TextView();
7+
8+
// Bind TextView to ObservableProperty
9+
observableProperty.addObserver(textView);
10+
11+
// Update ObservableProperty
12+
System.out.println("Setting value in ObservableProperty...");
13+
observableProperty.setValue("Hello, Design Patterns!");
14+
15+
// Simulate user input through TextView
16+
System.out.println("User updates TextView...");
17+
textView.setText("User Input!");
18+
19+
// Set another value in ObservableProperty to observe two-way binding
20+
System.out.println("Setting another value in ObservableProperty...");
21+
observableProperty.setValue("Two-Way Binding Works!");
22+
}
23+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.iluwatar.binding;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
public class ObservableProperty<T> {
7+
private T value;
8+
private final List<Observer<T>> observers = new ArrayList<>();
9+
private boolean isUpdating = false;
10+
11+
public ObservableProperty(T initialValue) {
12+
this.value = initialValue;
13+
}
14+
15+
public T getValue() {
16+
return value;
17+
}
18+
19+
public void setValue(T value) {
20+
if (isUpdating || this.value == value || (this.value != null && this.value.equals(value))) {
21+
return;
22+
}
23+
this.value = value;
24+
notifyObservers();
25+
}
26+
27+
public void addObserver(Observer<T> observer) {
28+
observers.add(observer);
29+
observer.bind(this);
30+
}
31+
32+
public void removeObserver(Observer<T> observer) {
33+
observers.remove(observer);
34+
observer.unbind();
35+
}
36+
37+
private void notifyObservers() {
38+
for (Observer<T> observer : observers) {
39+
isUpdating = true;
40+
observer.update(value);
41+
isUpdating = false;
42+
}
43+
}
44+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.iluwatar.binding;
2+
3+
public interface Observer<T> {
4+
void update(T newValue);
5+
6+
void bind(ObservableProperty<T> observableProperty);
7+
8+
void unbind();
9+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.iluwatar.binding;
2+
3+
public class TextView implements Observer<String> {
4+
private String text;
5+
private ObservableProperty<String> boundProperty;
6+
7+
@Override
8+
public void update(String newValue) {
9+
// Prevent unnecessary updates
10+
if (text != null && text.equals(newValue)) {
11+
return;
12+
}
13+
this.text = newValue;
14+
System.out.println("TextView updated: " + text);
15+
}
16+
17+
@Override
18+
public void bind(ObservableProperty<String> observableProperty) {
19+
// Unbind from the current property if already bound
20+
if (this.boundProperty != null) {
21+
this.boundProperty.removeObserver(this);
22+
}
23+
this.boundProperty = observableProperty;
24+
}
25+
26+
@Override
27+
public void unbind() {
28+
if (this.boundProperty != null) {
29+
this.boundProperty.removeObserver(this);
30+
this.boundProperty = null;
31+
}
32+
}
33+
34+
public void setText(String newText) {
35+
if (boundProperty != null) {
36+
boundProperty.setValue(newText);
37+
}
38+
this.text = newText;
39+
}
40+
41+
public String getText() {
42+
return text;
43+
}
44+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.iluwatar.binding;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
6+
7+
public class AppTest {
8+
9+
@Test
10+
public void testAppRunsWithoutExceptions() {
11+
assertDoesNotThrow(() -> {
12+
com.iluwatar.binding.App.main(new String[]{});
13+
});
14+
}
15+
}
16+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.iluwatar.binding;
2+
3+
import org.junit.jupiter.api.Test;
4+
import static org.junit.jupiter.api.Assertions.*;
5+
6+
public class ObservablePropertyTest {
7+
8+
@Test
9+
public void testNotifyObservers() {
10+
ObservableProperty<String> observableProperty = new ObservableProperty<>("Initial");
11+
TestObserver testObserver = new TestObserver();
12+
observableProperty.addObserver(testObserver);
13+
14+
observableProperty.setValue("Updated");
15+
16+
assertEquals("Updated", testObserver.getReceivedValue());
17+
}
18+
19+
private static class TestObserver implements Observer<String> {
20+
private String receivedValue;
21+
22+
@Override
23+
public void update(String newValue) {
24+
receivedValue = newValue;
25+
}
26+
27+
@Override
28+
public void bind(ObservableProperty<String> observableProperty) {
29+
// No binding logic needed for this test.
30+
}
31+
32+
@Override
33+
public void unbind() {
34+
// No unbinding logic needed for this test.
35+
}
36+
37+
public String getReceivedValue() {
38+
return receivedValue;
39+
}
40+
}
41+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.iluwatar.binding;
2+
3+
import org.junit.jupiter.api.Test;
4+
import static org.junit.jupiter.api.Assertions.*;
5+
6+
public class TextViewTest {
7+
8+
@Test
9+
public void testTextViewUpdatesFromObservable() {
10+
ObservableProperty<String> observableProperty = new ObservableProperty<>("Initial");
11+
TextView textView = new TextView();
12+
13+
// Bind TextView to ObservableProperty
14+
observableProperty.addObserver(textView);
15+
16+
// Change observable value
17+
observableProperty.setValue("New Value");
18+
19+
assertEquals("New Value", textView.getText());
20+
}
21+
22+
@Test
23+
public void testTextViewUpdatesObservable() {
24+
ObservableProperty<String> observableProperty = new ObservableProperty<>("Initial");
25+
TextView textView = new TextView();
26+
27+
// Bind TextView to ObservableProperty
28+
observableProperty.addObserver(textView);
29+
30+
// Simulate user updating TextView
31+
textView.setText("User Input");
32+
33+
assertEquals("User Input", observableProperty.getValue());
34+
}
35+
}

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
<module>prototype</module>
6767
<module>singleton</module>
6868
<module>adapter</module>
69+
<module>binding-property</module>
6970
<module>bridge</module>
7071
<module>composite</module>
7172
<module>data-access-object</module>

0 commit comments

Comments
 (0)