190 lines
6.0 KiB
Dart
190 lines
6.0 KiB
Dart
mixin Connectable {
|
||
bool isConnected = false;
|
||
void toggleConnection() => isConnected = !isConnected;
|
||
}
|
||
|
||
abstract class SmartDevice {
|
||
final String id;
|
||
String _name;
|
||
bool _powerStatus = false;
|
||
bool _auto_on_off = false;
|
||
|
||
static const String brand = "GeminiHome";
|
||
|
||
// 1. Standard Constructor with named parameters
|
||
SmartDevice({required this.id, required String name}) : _name = name;
|
||
|
||
// 2. Named Constructor: Preset for Office use
|
||
SmartDevice.office({required this.id})
|
||
: _name = "Office Desk Lamp",
|
||
_auto_on_off = true;
|
||
|
||
// 3. Named Constructor: Preset for Eco mode
|
||
SmartDevice.eco({required this.id})
|
||
: _name = "Eco Saver Unit",
|
||
_powerStatus = false,
|
||
_auto_on_off = true;
|
||
|
||
// 4. Factory Constructor using named parameters
|
||
factory SmartDevice.create({
|
||
required String type,
|
||
required String id,
|
||
required String name,
|
||
}) {
|
||
if (type == 'light') return SmartLight(id: id, name: name);
|
||
return SmartSpeaker(id: id, name: name);
|
||
}
|
||
|
||
// Specialized factory for the Office preset
|
||
factory SmartDevice.createOffice({required String id}) {
|
||
return SmartLight.office(id: id);
|
||
}
|
||
|
||
String get name => _name;
|
||
set name(String value) => _name = value.trim();
|
||
|
||
void performAction();
|
||
|
||
void togglePower() {
|
||
_powerStatus = !_powerStatus;
|
||
print('$_name is now ${_powerStatus ? "ON" : "OFF"} (Auto-mode: $_auto_on_off)');
|
||
}
|
||
}
|
||
|
||
class SmartLight extends SmartDevice {
|
||
int brightness = 100;
|
||
|
||
// Using 'super' syntax with named parameters
|
||
SmartLight({
|
||
required super.id,
|
||
required super.name
|
||
});
|
||
|
||
// Redirecting to parent named constructors
|
||
SmartLight.office({required String id}) : super.office(id: id);
|
||
|
||
SmartLight.eco({required String id}) : super.eco(id: id);
|
||
|
||
@override
|
||
void performAction() {
|
||
print('[$_name] Brightness: $brightness%');
|
||
}
|
||
}
|
||
|
||
class SmartSpeaker extends SmartDevice with Connectable {
|
||
double volume;
|
||
|
||
SmartSpeaker({
|
||
required String id,
|
||
required String name,
|
||
this.volume = 0.5,
|
||
}) : assert(volume >= 0 && volume <= 1.0),
|
||
super(id: id, name: name);
|
||
|
||
@override
|
||
void performAction() {
|
||
print('[$_name] Volume: ${(volume * 100).toInt()}%. Connected: $isConnected');
|
||
}
|
||
}
|
||
|
||
void main() {
|
||
// --- USAGE ---
|
||
|
||
// 1. Using the 'Office' preset with named ID
|
||
final workLamp = SmartLight.office(id: 'OFFICE_01');
|
||
|
||
// 2. Using the 'Eco' preset with named ID
|
||
final ecoLight = SmartLight.eco(id: 'ECO_99');
|
||
|
||
// 3. Standard creation using clear labels for ID and Name
|
||
final customLight = SmartLight(id: 'CUST_01', name: 'My Custom Light');
|
||
|
||
// 4. Using the factory with named parameters
|
||
final speaker = SmartDevice.create(
|
||
type: 'speaker',
|
||
id: 'SPK_01',
|
||
name: 'Living Room Bass'
|
||
);
|
||
|
||
print('--- Testing Presets ---');
|
||
workLamp.performAction();
|
||
workLamp.togglePower();
|
||
|
||
print('');
|
||
|
||
ecoLight.performAction();
|
||
ecoLight.togglePower();
|
||
|
||
print('');
|
||
speaker.performAction();
|
||
}
|
||
|
||
|
||
// 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?
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|