update ex_2
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
return SmartLight(id: id, name: name);
|
||||||
|
---
|
||||||
|
This is poor code naming.
|
||||||
|
you should use naming like this.
|
||||||
|
if user provides id and name: user_provided_id, user_provided_name
|
||||||
|
So I know the relationship.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,114 +1,192 @@
|
|||||||
|
// WHAT: A mixin is a set of fields and methods that can be reused by multiple classes.
|
||||||
|
// WHY: To allow 'SmartSpeaker' to have connection logic without forcing 'SmartLight' to have it.
|
||||||
mixin Connectable {
|
mixin Connectable {
|
||||||
|
// WHAT: A boolean instance variable initialized to false.
|
||||||
|
// WHY: To track whether a specific device is currently linked to a network or bluetooth.
|
||||||
bool isConnected = false;
|
bool isConnected = false;
|
||||||
|
|
||||||
|
// WHAT: A function using arrow syntax (=>) to negate the current boolean.
|
||||||
|
// WHY: Provides a standardized, reusable way to flip the connection state for any class using this mixin.
|
||||||
void toggleConnection() => isConnected = !isConnected;
|
void toggleConnection() => isConnected = !isConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WHAT: An abstract class acting as a base template.
|
||||||
|
// WHY: It defines the shared "DNA" of all devices but prevents anyone from creating a generic "SmartDevice".
|
||||||
abstract class SmartDevice {
|
abstract class SmartDevice {
|
||||||
|
// WHAT: A final String that can only be set once.
|
||||||
|
// WHY: Ensures the device's unique identifier remains immutable (unchangeable) after creation.
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
|
// WHAT: A private String (indicated by the underscore).
|
||||||
|
// WHY: Encapsulation; it hides raw data so we can control how it's accessed or formatted via getters/setters.
|
||||||
String _name;
|
String _name;
|
||||||
|
|
||||||
|
// WHAT: Private boolean state for power.
|
||||||
|
// WHY: To keep the "ON/OFF" state internal to the object, requiring a method call to change it.
|
||||||
bool _powerStatus = false;
|
bool _powerStatus = false;
|
||||||
|
|
||||||
|
// WHAT: Private boolean for automation.
|
||||||
|
// WHY: To track if the device should handle its own power timing without user intervention.
|
||||||
bool _auto_on_off = false;
|
bool _auto_on_off = false;
|
||||||
|
|
||||||
|
// WHAT: A static constant String.
|
||||||
|
// WHY: This belongs to the class itself (the brand), not the individual instances.
|
||||||
static const String brand = "GeminiHome";
|
static const String brand = "GeminiHome";
|
||||||
|
|
||||||
// 1. Standard Constructor with named parameters
|
// 1. Standard Constructor
|
||||||
|
// WHAT: A generative constructor using 'user_provided' naming.
|
||||||
|
// WHY: Maps the external input (user_provided_id) to the internal final field (id).
|
||||||
SmartDevice({
|
SmartDevice({
|
||||||
required this.id,
|
required String user_provided_id,
|
||||||
required String name
|
required String user_provided_name
|
||||||
}) : _name = name;
|
}) : id = user_provided_id,
|
||||||
|
_name = user_provided_name;
|
||||||
|
|
||||||
// 2. Named Constructor: Preset for Office use
|
// 2. Named Constructor: Office
|
||||||
SmartDevice.office({required this.id})
|
// WHAT: A specialized constructor for a specific use-case.
|
||||||
: _name = "Office Desk Lamp",
|
// WHY: It streamlines creation by hardcoding the name and auto-mode for office settings.
|
||||||
|
SmartDevice.office({
|
||||||
|
required String user_provided_id
|
||||||
|
}) : id = user_provided_id,
|
||||||
|
_name = "Office Desk Lamp",
|
||||||
_auto_on_off = true;
|
_auto_on_off = true;
|
||||||
|
|
||||||
// 3. Named Constructor: Preset for Eco mode
|
// 3. Named Constructor: Eco
|
||||||
SmartDevice.eco({required this.id})
|
// WHAT: A specialized constructor for power saving.
|
||||||
: _name = "Eco Saver Unit",
|
// WHY: It ensures the device starts in an "OFF" state with "Auto" mode enabled for efficiency.
|
||||||
|
SmartDevice.eco({
|
||||||
|
required String user_provided_id
|
||||||
|
}) : id = user_provided_id,
|
||||||
|
_name = "Eco Saver Unit",
|
||||||
_powerStatus = false,
|
_powerStatus = false,
|
||||||
_auto_on_off = true;
|
_auto_on_off = true;
|
||||||
|
|
||||||
// 4. Factory Constructor using named parameters
|
// 4. Factory Constructor
|
||||||
|
// WHAT: A factory that decides which subclass to create.
|
||||||
|
// WHY: It allows the "SmartDevice" class to return a "SmartLight" or "SmartSpeaker" based on a String input.
|
||||||
factory SmartDevice.create({
|
factory SmartDevice.create({
|
||||||
required String type,
|
required String user_provided_type,
|
||||||
required String id,
|
required String user_provided_id,
|
||||||
required String name,
|
required String user_provided_name,
|
||||||
}) {
|
}) {
|
||||||
if (type == 'light') return SmartLight(id: id, name: name);
|
// WHAT: Logic to check the 'type' string.
|
||||||
return SmartSpeaker(id: id, name: name);
|
// WHY: To determine which concrete implementation is needed for the user's request.
|
||||||
}
|
if (user_provided_type == 'light') {
|
||||||
|
return SmartLight(user_provided_id: user_provided_id, user_provided_name: user_provided_name);
|
||||||
// Specialized factory for the Office preset
|
}
|
||||||
factory SmartDevice.createOffice({required String id}) {
|
return SmartSpeaker(user_provided_id: user_provided_id, user_provided_name: user_provided_name);
|
||||||
return SmartLight.office(id: id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WHAT: A public Getter for the private name.
|
||||||
|
// WHY: Allows other parts of the app to read the name without being able to overwrite it directly.
|
||||||
String get name => _name;
|
String get name => _name;
|
||||||
set name(String value) => _name = value.trim();
|
|
||||||
|
|
||||||
|
// WHAT: A public Setter with a .trim() method.
|
||||||
|
// WHY: Cleans the input data (removing spaces) before saving it to the private '_name' field.
|
||||||
|
set name(String user_provided_new_name) => _name = user_provided_new_name.trim();
|
||||||
|
|
||||||
|
// WHAT: An abstract method signature (no curly braces).
|
||||||
|
// WHY: It forces every subclass to define what "performing an action" actually means for that specific device.
|
||||||
void performAction();
|
void performAction();
|
||||||
|
|
||||||
|
// WHAT: A concrete method shared by all subclasses.
|
||||||
|
// WHY: To avoid code duplication; every smart device uses the same logic to toggle power.
|
||||||
void togglePower() {
|
void togglePower() {
|
||||||
|
// WHAT: Boolean inversion.
|
||||||
|
// WHY: Changes 'true' to 'false' or vice versa to simulate a power switch.
|
||||||
_powerStatus = !_powerStatus;
|
_powerStatus = !_powerStatus;
|
||||||
print('$_name is now ${_powerStatus ? "ON" : "OFF"} (Auto-mode: $_auto_on_off)');
|
print('$_name is now ${_powerStatus ? "ON" : "OFF"} (Auto-mode: $_auto_on_off)');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WHAT: A concrete implementation of a SmartDevice.
|
||||||
|
// WHY: To add specific properties (like brightness) that only a light would have.
|
||||||
class SmartLight extends SmartDevice {
|
class SmartLight extends SmartDevice {
|
||||||
|
// WHAT: An integer specific to this class.
|
||||||
|
// WHY: To track how bright the light is; initialized to 100 by default.
|
||||||
int brightness = 100;
|
int brightness = 100;
|
||||||
|
|
||||||
// Using 'super' syntax with named parameters
|
// WHAT: Standard constructor passing data to the super class.
|
||||||
|
// WHY: To ensure a custom light gets its ID and Name correctly initialized in the base class.
|
||||||
SmartLight({
|
SmartLight({
|
||||||
required super.id,
|
required String user_provided_id,
|
||||||
required super.name
|
required String user_provided_name
|
||||||
});
|
}) : super(user_provided_id: user_provided_id, user_provided_name: user_provided_name);
|
||||||
|
|
||||||
// Redirecting to parent named constructors
|
// WHAT: Named constructor with an extra assignment in the initializer list.
|
||||||
SmartLight.office({required String id}) : super.office(id: id);
|
// WHY: It combines the "Office" defaults from the parent with a specific "50%" brightness for this subclass.
|
||||||
|
SmartLight.office({
|
||||||
|
required String user_provided_id
|
||||||
|
}) : brightness = 50, // Sets the brightness specifically for office use.
|
||||||
|
super.office(user_provided_id: user_provided_id); // Calls the parent's office logic.
|
||||||
|
|
||||||
SmartLight.eco({required String id}) : super.eco(id: id);
|
// WHAT: Named constructor for Eco mode.
|
||||||
|
// WHY: Redirects to the eco settings in the base class (e.g., auto-off enabled).
|
||||||
|
SmartLight.eco({
|
||||||
|
required String user_provided_id
|
||||||
|
}) : brightness = 20, // WHY: Maybe Eco mode should be even dimmer to save more power!
|
||||||
|
super.eco(user_provided_id: user_provided_id);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
// WHAT: Implementation of the abstract method.
|
||||||
|
// WHY: To display the specific state (brightness) of this light.
|
||||||
void performAction() {
|
void performAction() {
|
||||||
print('[$_name] Brightness: $brightness%');
|
print('[$_name] Brightness: $brightness%');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WHAT: A concrete implementation using inheritance (SmartDevice) and a mixin (Connectable).
|
||||||
|
// WHY: To create a device that has a name/id AND the ability to connect/disconnect.
|
||||||
class SmartSpeaker extends SmartDevice with Connectable {
|
class SmartSpeaker extends SmartDevice with Connectable {
|
||||||
|
// WHAT: A double (0.0 to 1.0) for audio level.
|
||||||
|
// WHY: Specific data needed only for audio-based devices.
|
||||||
double volume;
|
double volume;
|
||||||
|
|
||||||
|
// WHAT: Constructor with an assertion check.
|
||||||
|
// WHY: The 'assert' stops the program during debugging if the volume is out of bounds (0.0 - 1.0).
|
||||||
SmartSpeaker({
|
SmartSpeaker({
|
||||||
required String id,
|
required String user_provided_id,
|
||||||
required String name,
|
required String user_provided_name,
|
||||||
this.volume = 0.5,
|
this.volume = 0.5,
|
||||||
}) : assert(volume >= 0 && volume <= 1.0),
|
}) : assert(volume >= 0 && volume <= 1.0),
|
||||||
super(id: id, name: name);
|
super(user_provided_id: user_provided_id, user_provided_name: user_provided_name);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
// WHAT: Concrete implementation of the abstract method.
|
||||||
|
// WHY: Defines that for a Speaker, "performing an action" includes checking the connection status.
|
||||||
void performAction() {
|
void performAction() {
|
||||||
print('[$_name] Volume: ${(volume * 100).toInt()}%. Connected: $isConnected');
|
print('[$_name] Volume: ${(volume * 100).toInt()}%. Connected: $isConnected');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- EXECUTION ---
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// --- USAGE ---
|
// WHAT: Instantiating a Light using the Office preset.
|
||||||
|
// WHY: To demonstrate how named constructors simplify creating specific device configurations.
|
||||||
// 1. Using the 'Office' preset with named ID
|
final workLamp = SmartLight.office(user_provided_id: 'OFFICE_01');
|
||||||
final workLamp = SmartLight.office(id: 'OFFICE_01');
|
|
||||||
|
|
||||||
// 2. Using the 'Eco' preset with named ID
|
// WHAT: Instantiating a Light using the Eco preset.
|
||||||
final ecoLight = SmartLight.eco(id: 'ECO_99');
|
// WHY: Shows the reuse of the super.eco logic.
|
||||||
|
final ecoLight = SmartLight.eco(user_provided_id: 'ECO_99');
|
||||||
|
|
||||||
// 3. Standard creation using clear labels for ID and Name
|
// WHAT: Standard instantiation of a SmartLight.
|
||||||
final customLight = SmartLight(id: 'CUST_01', name: 'My Custom Light');
|
// WHY: Demonstrates creating an object with custom labels.
|
||||||
|
final customLight = SmartLight(
|
||||||
// 4. Using the factory with named parameters
|
user_provided_id: 'CUST_01',
|
||||||
final speaker = SmartDevice.create(
|
user_provided_name: 'My Custom Light'
|
||||||
type: 'speaker',
|
|
||||||
id: 'SPK_01',
|
|
||||||
name: 'Living Room Bass'
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// WHAT: Using the factory constructor.
|
||||||
|
// WHY: Shows how the 'SmartDevice' base class can act as a single entry point to create different types of objects.
|
||||||
|
final speaker = SmartDevice.create(
|
||||||
|
user_provided_type: 'speaker',
|
||||||
|
user_provided_id: 'SPK_01',
|
||||||
|
user_provided_name: 'Living Room Bass'
|
||||||
|
);
|
||||||
|
|
||||||
|
// WHAT: Printing and calling methods.
|
||||||
|
// WHY: To verify that all constructors and methods are working as expected.
|
||||||
print('--- Testing Presets ---');
|
print('--- Testing Presets ---');
|
||||||
workLamp.performAction();
|
workLamp.performAction();
|
||||||
workLamp.togglePower();
|
workLamp.togglePower();
|
||||||
@@ -123,57 +201,6 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// You're absolutely right. Understanding *why* we use these features is more important than just knowing the syntax. Let’s break down the rationale and real-world use cases for the structure I built.
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
// ## 1. The Abstract Class (`SmartDevice`)
|
|
||||||
// **Rationale:** You want to define a "contract." Every smart device must have an ID, a name, and an action, but a "generic" smart device doesn't exist in reality—it’s always a specific light, speaker, or thermostat.
|
|
||||||
|
|
||||||
// * **Use Case:** In a Large App, you might have a `List<SmartDevice> devices`. You can loop through this list and call `.togglePower()` on every item without knowing if it's a light or a speaker. This is the heart of **Polymorphism**.
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
// ## 2. Factory Constructors (`SmartDevice.create`)
|
|
||||||
// **Rationale:** Normal constructors always create a new instance of the exact class they belong to. A `factory` constructor is more flexible: it can return a subclass or even a cached instance.
|
|
||||||
|
|
||||||
// * **Use Case:** **API Parsing.** When you get JSON data from a server, you don't know the device type until you read the data. A factory constructor can look at the "type" field in the JSON and decide to return a `SmartLight` object or a `SmartSpeaker` object automatically.
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
// ## 3. Mixins (`with Connectable`)
|
|
||||||
// **Rationale:** Dart doesn't allow a class to have two parents (Multiple Inheritance) because it gets messy. Mixins allow you to "plug in" shared behavior across unrelated classes.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// * **Use Case:** Imagine you have a `SmartSpeaker` (a device) and a `PhoneApp` (not a device). Both need "WiFi Connection" logic. Instead of copying the code twice, you create a `Connectable` mixin and apply it to both.
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
// ## 4. Getters and Setters
|
|
||||||
// **Rationale:** This is about **Encapsulation**. You should never let outside code touch your internal variables (`_name`) directly. By using a setter, you can "sanitize" the data.
|
|
||||||
|
|
||||||
// * **Use Case:** Validation. If a user tries to set a device name to a string full of empty spaces, your setter `set name(value) => _name = value.trim();` automatically cleans it up before it’s saved to your database.
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
// ## 5. Redirecting Constructors (`SmartSpeaker.loud`)
|
|
||||||
// **Rationale:** To avoid "Boilerplate" (repetitive code). If you have a common configuration for a class, you don't want to rewrite the initialization logic.
|
|
||||||
|
|
||||||
// * **Use Case:** **Presets.** In a UI, you might have a "Default" button and a "Pro" button. Instead of manually setting 10 different parameters each time, you create a named constructor like `Button.primary()` that redirects to the main constructor with all the "Pro" settings pre-filled.
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
// ## 6. Static Members (`static const brand`)
|
|
||||||
// **Rationale:** Memory efficiency. If you have 1,000 smart lights, you don't need the string "GeminiHome" stored in memory 1,000 times.
|
|
||||||
|
|
||||||
// * **Use Case:** **Configuration & Constants.** Use `static` for things that are "Universal truth" for the class, such as a maximum volume limit or a shared API version number.
|
|
||||||
|
|
||||||
// Would you like me to show you how to handle **Interfaces** in Dart, which is the final piece of the OO puzzle?
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user