This commit is contained in:
2026-03-28 08:26:05 +07:00
parent 716ac863ae
commit abb417cff3
3 changed files with 96 additions and 96 deletions

View File

@@ -0,0 +1,219 @@
// 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 {
// 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;
// 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;
}
// 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 {
// 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;
// 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;
// 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;
// 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;
// WHAT: A static constant String.
// WHY: This belongs to the class itself (the brand), not the individual instances.
static const String brand = "GeminiHome";
// 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({
required String user_provided_id,
required String user_provided_name
}) : id = user_provided_id,
_name = user_provided_name;
// 2. Named Constructor: Office
// WHAT: A specialized constructor for a specific use-case.
// 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;
// 3. Named Constructor: Eco
// WHAT: A specialized constructor for power saving.
// 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,
_auto_on_off = true;
// 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({
required String user_provided_type,
required String user_provided_id,
required String user_provided_name,
}) {
// WHAT: Logic to check the 'type' string.
// 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);
}
return SmartSpeaker(user_provided_id: user_provided_id, user_provided_name: user_provided_name);
}
// 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;
// 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();
// 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() {
// WHAT: Boolean inversion.
// WHY: Changes 'true' to 'false' or vice versa to simulate a power switch.
_powerStatus = !_powerStatus;
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 {
// WHAT: An integer specific to this class.
// WHY: To track how bright the light is; initialized to 100 by default.
int brightness = 100;
// 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({
required String user_provided_id,
required String user_provided_name
}) : super(user_provided_id: user_provided_id, user_provided_name: user_provided_name);
// WHAT: Named constructor with an extra assignment in the initializer list.
// 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.
// 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
// WHAT: Implementation of the abstract method.
// WHY: To display the specific state (brightness) of this light.
void performAction() {
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 {
// WHAT: A double (0.0 to 1.0) for audio level.
// WHY: Specific data needed only for audio-based devices.
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({
required String user_provided_id,
required String user_provided_name,
this.volume = 0.5,
}) : assert(volume >= 0 && volume <= 1.0),
super(user_provided_id: user_provided_id, user_provided_name: user_provided_name);
@override
// WHAT: Concrete implementation of the abstract method.
// WHY: Defines that for a Speaker, "performing an action" includes checking the connection status.
void performAction() {
print('[$_name] Volume: ${(volume * 100).toInt()}%. Connected: $isConnected');
}
}
// --- EXECUTION ---
void main() {
// WHAT: Instantiating a Light using the Office preset.
// WHY: To demonstrate how named constructors simplify creating specific device configurations.
final workLamp = SmartLight.office(user_provided_id: 'OFFICE_01');
// WHAT: Instantiating a Light using the Eco preset.
// WHY: Shows the reuse of the super.eco logic.
final ecoLight = SmartLight.eco(user_provided_id: 'ECO_99');
// WHAT: Standard instantiation of a SmartLight.
// WHY: Demonstrates creating an object with custom labels.
final customLight = SmartLight(
user_provided_id: 'CUST_01',
user_provided_name: 'My Custom Light'
);
// 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 ---');
workLamp.performAction();
workLamp.togglePower();
print('');
ecoLight.performAction();
ecoLight.togglePower();
print('');
speaker.performAction();
}